遇见gtest--事件

1、前言
    在单元测试中,我们经常需要在某个测试套件、测试用例或者整个测试运行之前进行前置条件设置及检查,或者运行之后对运行结果进行校验等操作。在gtest中,称之为事件机制。gtest将事件按照作用的范围不同进行划分,从大到小总共分为3个层次:
    1)整个测试层面,即在测试工程开始前和结束后进行;
    2)测试套件层面,即在某个测试套件开始前和结束后进行;
    3)测试用例层面,即在某个测试用例开始前和结束后进行;

2、测试层面事件实现
    要实现测试层面的事件,我们需要继承testing::Environment类,首先我们来看一下这个类的定义:   
class Environment {
 public:
  virtual ~Environment() {}

  // Override this to define how to set up the environment.
  virtual void SetUp() {}

  // Override this to define how to tear down the environment.
  virtual void TearDown() {}
 private:
  struct Setup_should_be_spelled_SetUp {};
  virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }
};

    这个类中有两个虚函数:SetUp和TearDown。我们的子类只需要实现这个方法即可。其中在SetUp方法中实现所有测试启动之前需要完成的操作,而TearDown函数中实现所有测试运行结束后需要进行的操作。例如:
class GlobalEvent2 : 
 public testing::Environment
{
public:

 virtual void SetUp()
 {
  cout << "Before any case, Global 2" << endl;
 }

 virtual void TearDown()
 {
  cout << "After all cases done, Global 2" << endl;
 }
};

    然后,在main函数中,在RUN_ALL_TESTS()之前,我们调用如下语句:
    testing::AddGlobalTestEnvironment(new GlobalEnvent);
    将这个测试层面的的事件添加到事件列表即可。这样,在测试执行之前,系统会先执行GlobalEvent2的SetUp方法;在所有测试用例执行完之后,系统会执行GlobalEvent2的TearDown方法。另外,我们可以定义任意多个继承自testing::Environment的子类,以实现不同的全局事件。所有的子类的SetUp按照我们调用testing::AddGlobalTestEnvironment添加它们的先后顺序执行,而TearDown的执行顺序则与添加顺序相反。

3、测试套件层面事件
    要在测试套件层面上定义事件,我们需要继承testing::Test类,并覆盖它的静态方法:SetUpTestCase和TearDownTestCase.在继续之前我们首先看看testing::Test类的定义:
class GTEST_API_ Test {
 public:
  friend class TestInfo;

  // Defines types for pointers to functions that set up and tear down
  // a test case.
  typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc;
  typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc;

  // The d'tor is virtual as we intend to inherit from Test.
  virtual ~Test();

  // Sets up the stuff shared by all tests in this test case.
  //
  // Google Test will call Foo::SetUpTestCase() before running the first
  // test in test case Foo.  Hence a sub-class can define its own
  // SetUpTestCase() method to shadow the one defined in the super
  // class.
  static void SetUpTestCase() {}

  // Tears down the stuff shared by all tests in this test case.
  //
  // Google Test will call Foo::TearDownTestCase() after running the last
  // test in test case Foo.  Hence a sub-class can define its own
  // TearDownTestCase() method to shadow the one defined in the super
  // class.
  static void TearDownTestCase() {}

  // Returns true iff the current test has a fatal failure.
  static bool HasFatalFailure();

  // Returns true iff the current test has a non-fatal failure.
  static bool HasNonfatalFailure();

  // Returns true iff the current test has a (either fatal or
  // non-fatal) failure.
  static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }

  // Logs a property for the current test, test case, or for the entire
  // invocation of the test program when used outside of the context of a
  // test case.  Only the last value for a given key is remembered.  These
  // are public static so they can be called from utility functions that are
  // not members of the test fixture.  Calls to RecordProperty made during
  // lifespan of the test (from the moment its constructor starts to the
  // moment its destructor finishes) will be output in XML as attributes of
  // the  element.  Properties recorded from fixture's
  // SetUpTestCase or TearDownTestCase are logged as attributes of the
  // corresponding  element.  Calls to RecordProperty made in the
  // global context (before or after invocation of RUN_ALL_TESTS and from
  // SetUp/TearDown method of Environment objects registered with Google
  // Test) will be output as attributes of the  element.
  static void RecordProperty(const std::string& key, const std::string& value);
  static void RecordProperty(const std::string& key, int value);

 protected:
  // Creates a Test object.
  Test();

  // Sets up the test fixture.
  virtual void SetUp();

  // Tears down the test fixture.
  virtual void TearDown();

 private:
  // Returns true iff the current test has the same fixture class as
  // the first test in the current test case.
  static bool HasSameFixtureClass();

  // Runs the test after the test fixture has been set up.
  //
  // A sub-class must implement this to define the test logic.
  //
  // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM.
  // Instead, use the TEST or TEST_F macro.
  virtual void TestBody() = 0;

  // Sets up, executes, and tears down the test.
  void Run();

  // Deletes self.  We deliberately pick an unusual name for this
  // internal method to avoid clashing with names used in user TESTs.
  void DeleteSelf_() { delete this; }

  // Uses a GTestFlagSaver to save and restore all Google Test flags.
  const internal::GTestFlagSaver* const gtest_flag_saver_;

  // Often a user mis-spells SetUp() as Setup() and spends a long time
  // wondering why it is never called by Google Test.  The declaration of
  // the following method is solely for catching such an error at
  // compile time:
  //
  //   - The return type is deliberately chosen to be not void, so it
  //   will be a conflict if a user declares void Setup() in his test
  //   fixture.
  //
  //   - This method is private, so it will be another compiler error
  //   if a user calls it from his test fixture.
  //
  // DO NOT OVERRIDE THIS FUNCTION.
  //
  // If you see an error about overriding the following function or
  // about it being private, you have mis-spelled SetUp() as Setup().
  struct Setup_should_be_spelled_SetUp {};
  virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; }

  // We disallow copying Tests.
  GTEST_DISALLOW_COPY_AND_ASSIGN_(Test);
};

    从testing::Test类的声明可以看到,SetUpTestCase和TearDownTestCase为静态方法,并且它们的注释也很详细。在测试套件的第一个测试用例开始前,SetUpTestCase函数会被调用,而在测试套件中的最后一个测试用例运行结束后,TearDownTestCase函数会被调用。

4、测试用例层面的事件
    要实现单个测试用例的事件,我们需要同样需要继承testing::Test类,并实现它的protected virtual方法SetUp和TearDown。gtest在运行这个测试用例之前,会首先调用SetUp方法,然后在测试用例结束之后,调用TearDown方法。

5、总结
    通过gtest事件机制,我们可以让gtest在运行测试、测试套件、测试用例的前后分别运行指定的代码段。这一点很有用,比如在单元测试中,我们可以将初始化操作放入SetUp函数中,而资源回收等操作方在TearDown函数中实现,这样可以使得我们在测试用例中只需专注于测试即可。

你可能感兴趣的:(C++)