GoogleTest 为每个测试创建一个新的测试夹具对象,以使测试独立且更易于调试。然而,有时测试使用的资源设置成本高昂,使得每个测试一个副本的模式成本太高,难以承受。
因此,GoogleTest 提供了共享单个资源副本的方式。除了每个测试 set-up/tear-down
之外,GoogleTest 还支持 per-test-suite 的 set-up/tear-down
。使用方法:
- 在 test fixture class(例如
FooTest
)中,通过 static 声明一些成员变量来保存共享资源。 - 在 test fixture class 之外(通常就在它的下面),定义这些成员变量,同时给它们赋初始值。
- 在同一个 test fixture class 中,定义一个公共成员函数
static void SetUpTestSuite()
(记住不要将其拼写为SetupTestSuite
,不是小写的字母u
!)来设置共享资源,以及通过定义一个static void TearDownTestSuite()
将其资源释放的函数。
共享资源生效的范围
GoogleTest在SetUpTestSuite()
运行 测试套件中的第一个测试FooTest
之前(即创建第一个 FooTest
对象之前)自动调用,而TearDownTestSuite()
是在运行其中的最后一个测试FooTest
之后(即删除最后一个对象之后)调用。在这之间,测试可以使用共享资源。
注意!
由于测试顺序是未定义的,因此代码不能依赖于另一个测试之前或之后的测试。此外,测试不得修改任何共享资源的状态,或者,如果它们确实修改了状态,则必须在将控制权传递给下一个测试之前将状态恢复为其原始值。
同时,SetUpTestSuite()
对于具有派生类的测试夹具类,可能会调用多次,因此不能认为函数体中的代码仅运行一次。此外,派生类仍然可以访问定义为静态成员的共享资源。因此,在管理共享资源时需要仔细考虑,以避免在TearDownTestSuite()
中,共享资源由于没有正确释放而发生内存泄漏。
以下是每个测试套件(per-test-suite) set-up 和 tear-down 的示例:
class FooTest : public testing::Test {
protected:
// Per-test-suite set-up.
// Called before the first test in this test suite.
// Can be omitted if not needed.
static void SetUpTestSuite() {
shared_resource_ = new ...;
// If `shared_resource_` is **not deleted** in `TearDownTestSuite()`,
// reallocation should be prevented because `SetUpTestSuite()` may be called
// in subclasses of FooTest and lead to memory leak.
//
// if (shared_resource_ == nullptr) {
// shared_resource_ = new ...;
// }
}
// Per-test-suite tear-down.
// Called after the last test in this test suite.
// Can be omitted if not needed.
static void TearDownTestSuite() {
delete shared_resource_;
shared_resource_ = nullptr;
}
// You can define per-test set-up logic as usual.
void SetUp() override { ... }
// You can define per-test tear-down logic as usual.
void TearDown() override { ... }
// Some expensive resource shared by all tests.
static T* shared_resource_;
};
T* FooTest::shared_resource_ = nullptr;
TEST_F(FooTest, Test1) {
... you can refer to shared_resource_ here ...
}
TEST_F(FooTest, Test2) {
... you can refer to shared_resource_ here ...
}
全局的 Set-Up 和 Tear-Down
除了可以在测试等级和测试套件级别进行 Set-Up 和 Tear-Down 一样,同样也可以在测试程序的级别进行 Set-Up 和 Tear-Down 。
如果这里想对::testing::Environment
类进行子类化以定义一个测试环境,下面是如何 Set-Up 和 Tear-Down 的示例:
class Environment : public ::testing::Environment {
public:
~Environment() override {}
// Override this to define how to set up the environment.
void SetUp() override {}
// Override this to define how to tear down the environment.
void TearDown() override {}
};
然后,通过调用以下函数向 GoogleTest 注册环境类的实例::testing::AddGlobalTestEnvironment()
:
Environment* AddGlobalTestEnvironment(Environment* env);
当RUN_ALL_TESTS()
调用时,它首先调用该SetUp()
方法。然后执行测试,前提是没有任何环境报告致命故障并且GTEST_SKIP()
尚未调用。最后,TearDown()
才被调用。
值得注意的是,仅当至少要执行一项测试时,才会调用 SetUp()
和TearDown()
。即使由于致命错误或 GTEST_SKIP()
而未运行测试,TearDown()
也会执行。
每次是否迭代的调用SetUp()
和TearDown()
取决于标志位 gtest_recreate_environments_when_repeating
。当每次需要重新创建对象时,会为每个环境对象调用SetUp()
和TearDown()
。但是,如果测试环境不需要每次迭代的时候创建,SetUp()
则仅在第一次迭代时调用,并且TearDown()
仅在最后一次迭代时调用。
注册多个环境对象当然也是被允许的。在测试套件中,SetUp()
将按照注册的顺序被调用, TearDown()
则会以相反的顺序被调用。
注意
由于GoogleTest 拥有已注册环境对象的所有权。因此,请勿自行删除它们。
在调用 RUN_ALL_TESTS()
之前,我们应该在 main()
函数中调用 AddGlobalTestEnvironment()
。如果你使用 gtest_main
,你需要在 main()
开始之前调用它才能生效。一种方法是定义一个全局变量,像这样:
testing::Environment* const foo_env =
testing::AddGlobalTestEnvironment(new FooEnvironment);
官方的文档强烈建议用户自己编写main()
,并在那里调用 AddGlobalTestEnvironment()
函数,因为依赖全局变量的初始化会使代码更难以阅读。同时,当用户从不同的翻译单元注册多个环境,并且这些环境之间存在依赖关系时可能会导致问题(编译器并不保证来自不同翻译单元的全局变量的初始化顺序)。