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函数中实现,这样可以使得我们在测试用例中只需专注于测试即可。