如何在带有多个测试.cpp文件的头文件中使用INSTANTIATE_TEST_CASE_P?

5
假设我在头文件fixtures.h中定义了一个GTest测试夹具:
 class baseFixture : public ::testing::Test{
   // some shared functionality among tests
 }

除了派生的装置可以允许一些参数化:

class derivedFixture: public baseFixture, 
  public ::testing::WithParamInterface<std::tuple<bool, int>>{};

我希望与INSTANTIATE_TEST_CASE_P一起使用,以对分散在N个.cpp文件中的大量测试进行参数化。我想在头文件中编写:

INSTANTIATE_TEST_CASE_P(derivedTests, derivedFixture, 
  ::testing::Combine(::testing::Bool(), ::testing::Values(1));

为了运行 {true, false} 和 {1} 的交叉产品的测试集。当我只编译一个 cpp 文件,称之为 N1.cpp,并运行可执行文件时,我的 derivedFixture* 测试以正确的行为运行两次。然而,当我构建整个项目并执行我的测试时,每个测试运行 2 * N 次。我在头文件中使用了 include guards 来防止 INSTANTIATE 宏被调用两次,并确信我没有在任何其他地方调用它。
1个回答

4
你正在做的实质上是这样的:
fixture.hpp (1)
#ifndef FIXTURE_HPP
#define FIXTURE_HPP

#include <gtest/gtest.h>

struct fixture: ::testing::TestWithParam<std::tuple<bool, int>>
{};

INSTANTIATE_TEST_CASE_P(instantiation_one, fixture,
  ::testing::Combine(::testing::Bool(), ::testing::Values(1)));

#endif

t1.cpp

#include "fixture.hpp"
#include <tuple>

TEST_P(fixture, test_a)
{
    auto const & param = GetParam();
    std::cout << "param 0 = " << std::get<0>(param) << std::endl;
    std::cout << "param 1 = " << std::get<1>(param) << std::endl;
    SUCCEED();
}

t2.cpp

#include "fixture.hpp"
#include <tuple>

TEST_P(fixture, test_b)
{
    auto const & param = GetParam();
    std::cout << "param 0 = " << std::get<0>(param) << std::endl;
    std::cout << "param 1 = " << std::get<1>(param) << std::endl;
    SUCCEED();
}

main.cpp (1)

#include <gtest/gtest.h>

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

编译、链接和运行:

$ ./gtester
[==========] Running 8 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 8 tests from instantiation_one/fixture
[ RUN      ] instantiation_one/fixture.test_a/0
param 0 = 0
param 1 = 1
[       OK ] instantiation_one/fixture.test_a/0 (0 ms)
[ RUN      ] instantiation_one/fixture.test_a/1
param 0 = 1
param 1 = 1
[       OK ] instantiation_one/fixture.test_a/1 (0 ms)
[ RUN      ] instantiation_one/fixture.test_a/0
param 0 = 0
param 1 = 1
[       OK ] instantiation_one/fixture.test_a/0 (0 ms)
[ RUN      ] instantiation_one/fixture.test_a/1
param 0 = 1
param 1 = 1
[       OK ] instantiation_one/fixture.test_a/1 (0 ms)
[ RUN      ] instantiation_one/fixture.test_b/0
param 0 = 0
param 1 = 1
[       OK ] instantiation_one/fixture.test_b/0 (0 ms)
[ RUN      ] instantiation_one/fixture.test_b/1
param 0 = 1
param 1 = 1
[       OK ] instantiation_one/fixture.test_b/1 (0 ms)
[ RUN      ] instantiation_one/fixture.test_b/0
param 0 = 0
param 1 = 1
[       OK ] instantiation_one/fixture.test_b/0 (0 ms)
[ RUN      ] instantiation_one/fixture.test_b/1
param 0 = 1
param 1 = 1
[       OK ] instantiation_one/fixture.test_b/1 (0 ms)
[----------] 8 tests from instantiation_one/fixture (0 ms total)

[----------] Global test environment tear-down
[==========] 8 tests from 1 test case ran. (1 ms total)
[  PASSED  ] 8 tests.

当你期望只有4个测试时,却看到了8个测试,每个instantiation_one/fixture.test_a/N都被运行了两次,其中N为{0,1}。

这个bug的问题在于:

INSTANTIATE_TEST_CASE_P(instantiation_one, fixture,
  ::testing::Combine(::testing::Bool(), ::testing::Values(1)));

fixture.hpp中,该文件被#include并在每个翻译单元中重复出现,导致此代码注册的两个参数化测试在运行时会被注册N次,因此会运行N次。我们应该为给定值集合的每个实例化值参数化 fixture 只编译一次,因此只需在一个源文件中完成,例如:fixture.hpp(2)
#ifndef FIXTURE_HPP
#define FIXTURE_HPP

#include <gtest/gtest.h>

struct fixture: ::testing::TestWithParam<std::tuple<bool, int>>
{};


#endif
#include <gtest/gtest.h>
#include "fixture.hpp"

INSTANTIATE_TEST_CASE_P(instantiation_one, fixture,
  ::testing::Combine(::testing::Bool(), ::testing::Values(1)));

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

重新编译、链接并运行:

$ g++ -Wall -Wextra -c main.cpp t1.cpp t2.cpp
$ g++ -o gtester main.o t1.o t2.o -lgtest -pthread
$ ./gtester
[==========] Running 4 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 4 tests from instantiation_one/fixture
[ RUN      ] instantiation_one/fixture.test_a/0
param 0 = 0
param 1 = 1
[       OK ] instantiation_one/fixture.test_a/0 (0 ms)
[ RUN      ] instantiation_one/fixture.test_a/1
param 0 = 1
param 1 = 1
[       OK ] instantiation_one/fixture.test_a/1 (0 ms)
[ RUN      ] instantiation_one/fixture.test_b/0
param 0 = 0
param 1 = 1
[       OK ] instantiation_one/fixture.test_b/0 (1 ms)
[ RUN      ] instantiation_one/fixture.test_b/1
param 0 = 1
param 1 = 1
[       OK ] instantiation_one/fixture.test_b/1 (0 ms)
[----------] 4 tests from instantiation_one/fixture (1 ms total)

[----------] Global test environment tear-down
[==========] 4 tests from 1 test case ran. (1 ms total)
[  PASSED  ] 4 tests.

也许我对翻译单元有误解。我认为 fixture.hpp 中的 #ifndef 保护会导致宏仅运行一次。既然不是这种情况,为什么会运行多次? - Philip Dakin
@PhilipDakin 你好。头文件保护可以防止在一个翻译单元中对foo.h的内容进行预处理和编译多次,无论foo.h#include了多少次。如果您编译了N个翻译单元,每个单元都包含了#include foo.h并成功将N个目标文件链接到一个程序中,那么由于编译foo.h内容而生成的任何代码都将被链接到该程序中N次。 - Mike Kinghan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接