【C++ UnitTest】基于Google Test 之如何在同一测试套件中的测试之间共享资源

GoogleTest 为每个测试创建一个新的测试夹具对象,以使测试独立且更易于调试。然而,有时测试使用的资源设置成本高昂,使得每个测试一个副本的模式成本太高,难以承受。
因此,GoogleTest 提供了共享单个资源副本的方式。除了每个测试 set-up/tear-down 之外,GoogleTest 还支持 per-test-suiteset-up/tear-down。使用方法:

  1. 在 test fixture class(例如FooTest)中,通过 static 声明一些成员变量来保存共享资源。
  2. 在 test fixture class 之外(通常就在它的下面),定义这些成员变量,同时给它们赋初始值。
  3. 在同一个 test fixture class 中,定义一个公共成员函数static void SetUpTestSuite()(记住不要将其拼写为SetupTestSuite,不是小写的字母u!)来设置共享资源,以及通过定义一个static void TearDownTestSuite()将其资源释放的函数。

共享资源生效的范围

GoogleTest在SetUpTestSuite()运行 测试套件中的第一个测试FooTest之前(即创建第一个 FooTest对象之前)自动调用,而TearDownTestSuite()是在运行其中的最后一个测试FooTest之后(即删除最后一个对象之后)调用。在这之间,测试可以使用共享资源。

注意!

由于测试顺序是未定义的,因此代码不能依赖于另一个测试之前或之后的测试。此外,测试不得修改任何共享资源的状态,或者,如果它们确实修改了状态,则必须在将控制权传递给下一个测试之前将状态恢复为其原始值。
同时,SetUpTestSuite()对于具有派生类的测试夹具类,可能会调用多次,因此不能认为函数体中的代码仅运行一次。此外,派生类仍然可以访问定义为静态成员的共享资源。因此,在管理共享资源时需要仔细考虑,以避免在TearDownTestSuite()中,共享资源由于没有正确释放而发生内存泄漏。

以下是每个测试套件(per-test-suite) set-uptear-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-UpTear-Down 一样,同样也可以在测试程序的级别进行 Set-UpTear-Down

如果这里想对::testing::Environment类进行子类化以定义一个测试环境,下面是如何 Set-UpTear-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()函数,因为依赖全局变量的初始化会使代码更难以阅读。同时,当用户从不同的翻译单元注册多个环境,并且这些环境之间存在依赖关系时可能会导致问题(编译器并不保证来自不同翻译单元的全局变量的初始化顺序)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值