引言
在《Google C++单元测试框架(Gtest)系列教程之三——测试固件(Test fixture)》中,介绍了如何使用测试固件为测试实例(Tests)进行数据配置和初始化。除了数据初始化方法,Gtest还提供了测试实例间共享数据的方法。
同属一个测试用例的测试实例间共享数据
为实现测试实例间的独立性,Gtest提供了为每个测试实例新生成一个测试固件对象的方法,通过“独享”测试对象中的数据,保证了一个测试实例的执行不会对其他实例的执行产生影响。但是,对于以下情况:
- 初始化数据涉及内存申请等操作,为每个测试实例构造对象将带来较大系统开销;
- 存在某数据,其在每个实例中均被用到,但每个实例都不会更改该数据的值;
我们就没有必要将这些数据放入测试固件对象中了,在一个C++类中,使用什么方法能让数据独立于对象之外、又能被对象访问呢?
没错!就是使用C++类的static成员变量,具体定义方法如下:
- 在测试固件类中,将需要在测试实例间共享的数据声明为static类型;
- 定义函数SetUpTestCase(),用于初始化共享数据;定义函数TearDownTestCase(),用于清理共享数据。这两个函数的函数类型均为static void。
在测试程序执行时,Gtest在第一个测试实例运行之前调用SetUpTestCase(),在最后一个测试实例运行之后调用TearDownTestCase(),在此期间,测试实例可以使用所定义的共享数据。
我们来看一个在测试实例间共享数据的实例:
class FooTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
shared_resource_ = new ...;
}
static void TearDownTestCase() {
delete shared_resource_;
shared_resource_ = NULL;
}
// You can define per-test set-up and tear-down logic as usual.
virtual void SetUp() { ... }
virtual void TearDown() { ... }
// Some expensive resource shared by all tests.
static T* shared_resource_;
};
T* FooTest::shared_resource_ = NULL;
TEST_F(FooTest, Test1) {
... you can refer to shared_resource here ...
}
TEST_F(FooTest, Test2) {
... you can refer to shared_resource here ...
}
由于Gtest不一定按照我们声明的顺序执行测试实例,因此我们的测试实例仍需保持独立,避免滥用共享数据造成的测试实例相互依赖的情况。在测试实例中,要不不改变共享数据的值,要不在改变数据值之后、执行完之前还原共享数据的值。
程序级别数据共享
正如前面介绍的,Gtest提供了测试实例(Tests)和测试用例(Test Case)级别的数据管理的方法,除此之外,Gtest在程序级别提供了共享数据管理方法,使得我们可以在测试用例和测试实例间共享数据。
为实现在程序级别共享数据,首先我们需要定义一个继承自::testing::Environment的类,并且编写SetUp()和TearDown()函数覆盖父类中的相应虚函数,该类中的成员变量即可作为共享数据:
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() {}
};
其次,调用以下接口“注册”类对象:
Environment* AddGlobalTestEnvironment(Environment* env);
其参数为“注册”前的类对象指针,返回值为“注册”后的类对象指针。下面我们来看实例:
class FooEnvironment: public testing::Environment
{
public:
virtual void SetUp()
{
printf("Environment SetUp!\n");
a = 100;
}
virtual void TearDown()
{
printf("Environment TearDown!\n");
}
int a; //共享数据
};
FooEnvironment* foo_env; //对象指针声明
TEST(firstTest, first) //访问共享数据并改变它的值
{
printf("in the firstTest, foo_env->p is %d\n", foo_env->a);
foo_env->a ++;
}
TEST(secondTest, second) //访问共享数据
{
printf("in the secondTest, foo_env->p is %d\n", foo_env->a);
}
int main(int argc, char* argv[])
{
foo_env = new FooEnvironment;
testing::AddGlobalTestEnvironment(foo_env); //注册
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
上面的测试用例firstTest和secondTest均访问了共享数据,作为演示,firstTest修改了共享数据的值。该测试程序执行结果如下:
[==========] Running 2 tests from 2 test cases.
[----------] Global test environment set-up.
Environment SetUp!
[----------] 1 test from firstTest
[ RUN ] firstTest.first
in the firstTest, foo_env->p is 100
[ OK ] firstTest.first (0 ms)
[----------] 1 test from firstTest (0 ms total)
[----------] 1 test from secondTest
[ RUN ] secondTest.second
in the secondTest, foo_env->p is 101
[ OK ] secondTest.second (0 ms)
[----------] 1 test from secondTest (0 ms total)
[----------] Global test environment tear-down
Environment TearDown!
[==========] 2 tests from 2 test cases ran. (0 ms total)
[ PASSED ] 2 tests.
小结
在Gtest中,测试实例间共享数据的方式有两种(除全局变量的方式外),一种是让同属一个测试用例的测试实例间共享数据,另一种是可让程序中的所有测试实例共享数据。测试实例间进行数据共享,破坏了测试实例间执行的独立性,个人认为应该尽量避免使用。
Reference: googletest project