GoogleTest 常见问题解答

为什么测试套件名称和测试名称不应包含下划线?

注意:除了以下理由外,GoogleTest 保留下划线 (_) 用于特殊用途的关键字,例如 DISABLED_ 前缀

下划线 (_) 很特殊,因为 C++ 保留以下内容供编译器和标准库使用

  1. 任何以下划线 (_) 后跟大写字母开头的标识符,以及
  2. 任何名称中任何位置包含两个连续下划线(即__)的标识符。

禁止用户代码使用此类标识符。

现在让我们看看这对 TESTTEST_F 意味着什么。

目前 TEST(TestSuiteName, TestName) 生成一个名为 TestSuiteName_TestName_Test 的类。如果 TestSuiteNameTestName 包含 _ 会发生什么?

  1. 如果 TestSuiteName 以下划线 (_) 后跟大写字母开头(例如,_Foo),我们将得到 _Foo_TestName_Test,这是保留的,因此无效。
  2. 如果 TestSuiteName_ 结尾(例如,Foo_),我们将得到 Foo__TestName_Test,这是无效的。
  3. 如果 TestName 以下划线 (_) 开头(例如,_Bar),我们将得到 TestSuiteName__Bar_Test,这是无效的。
  4. 如果 TestName_ 结尾(例如,Bar_),我们将得到 TestSuiteName_Bar__Test,这是无效的。

所以很明显 TestSuiteNameTestName 不能以 _ 开头或结尾(实际上,TestSuiteName 可以以 _ 开头——只要 _ 后面没有大写字母。但这变得复杂了。所以为了简单起见,我们只是说它不能以 _ 开头。)。

对于 TestSuiteNameTestName 在中间包含 _ 似乎没问题。但是,考虑一下这个

TEST(Time, Flies_Like_An_Arrow) { ... }
TEST(Time_Flies, Like_An_Arrow) { ... }

现在,这两个 TEST 将生成相同的类(Time_Flies_Like_An_Arrow_Test)。这不好。

因此,为了简单起见,我们只是要求用户避免在 TestSuiteNameTestName 中使用 _。该规则比必要的更严格,但它简单且易于记住。它还为 GoogleTest 提供了一些回旋余地,以防其实现将来需要更改。

如果您违反该规则,可能不会立即产生后果,但您的测试可能会(仅仅可能)在使用新编译器(或您正在使用的新版本的编译器)或使用新版本的 GoogleTest 时崩溃。因此,最好遵循该规则。

为什么 GoogleTest 支持 EXPECT_EQ(NULL, ptr)ASSERT_EQ(NULL, ptr),但不支持 EXPECT_NE(NULL, ptr)ASSERT_NE(NULL, ptr)

首先,您可以将 nullptr 与这些宏中的每一个一起使用,例如 EXPECT_EQ(ptr, nullptr)EXPECT_NE(ptr, nullptr)ASSERT_EQ(ptr, nullptr)ASSERT_NE(ptr, nullptr)。这是样式指南中的首选语法,因为 nullptr 没有 NULL 存在的类型问题。

由于 C++ 的一些特殊性,它需要一些重要的模板元编程技巧来支持使用 NULL 作为 EXPECT_XX()ASSERT_XX() 宏的参数。因此,我们只在最需要的地方这样做(否则我们会使 GoogleTest 的实现更难维护并且更容易出错,而不是必要的)。

从历史上看,EXPECT_EQ() 宏将期望值作为其第一个参数,将实际值作为第二个参数,尽管现在不鼓励这种参数顺序。有人希望编写 EXPECT_EQ(NULL, some_expression) 是合理的,并且确实多次被要求。因此,我们实现了它。

EXPECT_NE(NULL, ptr) 的需求并没有那么强烈。当断言失败时,您已经知道 ptr 必须是 NULL,因此在这种情况下打印 ptr 不会添加任何信息。这意味着 EXPECT_TRUE(ptr != NULL) 也能很好地工作。

如果我们支持 EXPECT_NE(NULL, ptr),为了保持一致性,我们还必须支持 EXPECT_NE(ptr, NULL)。这意味着在实现中使用模板元编程技巧两次,使其更难理解和维护。我们认为收益不值得付出代价。

最后,随着 gMock 匹配器库的增长,我们鼓励人们在测试中更频繁地使用统一的 EXPECT_THAT(value, matcher) 语法。匹配器方法的一个显着优势是,可以轻松组合匹配器以形成新的匹配器,而 EXPECT_NE 等宏则无法轻松组合。因此,我们希望在匹配器上投入更多精力,而不是在 EXPECT_XX() 宏上投入更多精力。

我需要测试接口的不同实现是否满足一些共同的要求。我应该使用类型测试还是值参数化测试?

对于测试同一接口的各种实现,类型测试或值参数化测试都可以完成。这实际上取决于您用户来决定哪种方法对您来说更方便,具体取决于您的特定情况。一些粗略的指导原则

我希望我没有让您更加困惑。 :-) 如果您不介意,我建议您尝试这两种方法。实践是掌握这两种工具之间细微差别的更好方法。一旦您获得了一些具体的经验,您就可以更轻松地决定下次使用哪一种。

我的死亡测试修改了一些状态,但死亡测试完成后,更改似乎丢失了。为什么?

死亡测试(EXPECT_DEATH 等)在子进程中执行,这样预期的崩溃就不会杀死测试程序(即父进程)。因此,它们产生的任何内存中的副作用都可以在它们各自的子进程中观察到,但在父进程中观察不到。您可以将它们视为或多或少在平行宇宙中运行。

特别是,如果您使用模拟并且死亡测试语句调用了一些模拟方法,则父进程会认为从未发生过这些调用。因此,您可能希望将您的 EXPECT_CALL 语句移动到 EXPECT_DEATH 宏中。

EXPECT_EQ(htonl(blah), blah_blah) 在优化模式下会生成奇怪的编译器错误。这是 GoogleTest 的 bug 吗?

实际上,bug 在于 htonl()

根据 'man htonl'htonl() 是一个函数,这意味着使用 htonl 作为函数指针是有效的。然而,在优化模式下,htonl() 被定义为一个,这破坏了这种用法。

更糟糕的是,htonl() 的宏定义使用了 gcc 扩展,并且不是标准 C++。这种 hack 的实现有一些特别的限制。特别是,它阻止你编写 Foo<sizeof(htonl(x))>(),其中 Foo 是一个具有整数参数的模板。

EXPECT_EQ(a, b) 的实现使用 sizeof(... a ...) 在模板参数内部,因此当 a 包含对 htonl() 的调用时,在优化模式下无法编译。很难使 EXPECT_EQ 绕过 htonl() 的 bug,因为解决方案必须适用于各种平台上的不同编译器。

编译器抱怨某些静态 const 成员变量的“未定义的引用”,但我确实在类体中定义了它们。怎么回事?

如果你的类有一个静态数据成员

// foo.h
class Foo {
  ...
  static const int kBar = 100;
};

你还需要在类体之外foo.cc 中定义它

const int Foo::kBar;  // No initializer here.

否则你的代码是无效的 C++,并且可能会以意想不到的方式崩溃。特别是,在 GoogleTest 比较断言(EXPECT_EQ 等)中使用它将生成“未定义的引用”链接器错误。“它以前有效”并不意味着它是有效的。这仅仅意味着你很幸运。 :-)

如果静态数据成员的声明是 constexpr,那么它隐式地是一个 inline 定义,并且不需要在 foo.cc 中进行单独的定义

// foo.h
class Foo {
  ...
  static constexpr int kBar = 100;  // Defines kBar, no need to do it in foo.cc.
};

我可以从另一个测试夹具派生一个测试夹具吗?

可以。

每个测试夹具都有一个对应的同名测试套件。这意味着只有一个测试套件可以使用特定的夹具。然而,有时多个测试用例可能想要使用相同或略有不同的夹具。例如,你可能想要确保 GUI 库的所有测试套件都不会泄漏重要的系统资源,如字体和画笔。

在 GoogleTest 中,你通过将共享逻辑放在一个基础测试夹具中,然后为每个想要使用此公共逻辑的测试套件从该基础派生一个单独的夹具,从而在测试套件之间共享一个夹具。然后,你使用 TEST_F() 来编写使用每个派生夹具的测试。

通常,你的代码看起来像这样

// Defines a base test fixture.
class BaseTest : public ::testing::Test {
 protected:
  ...
};

// Derives a fixture FooTest from BaseTest.
class FooTest : public BaseTest {
 protected:
  void SetUp() override {
    BaseTest::SetUp();  // Sets up the base fixture first.
    ... additional set-up work ...
  }

  void TearDown() override {
    ... clean-up work for FooTest ...
    BaseTest::TearDown();  // Remember to tear down the base fixture
                           // after cleaning up FooTest!
  }

  ... functions and variables for FooTest ...
};

// Tests that use the fixture FooTest.
TEST_F(FooTest, Bar) { ... }
TEST_F(FooTest, Baz) { ... }

... additional fixtures derived from BaseTest ...

如果需要,你可以继续从派生夹具派生测试夹具。GoogleTest 对层次结构的深度没有限制。

有关使用派生测试夹具的完整示例,请参阅 sample5_unittest.cc

我的编译器抱怨“void value not ignored as it ought to be”。这意味着什么?

你可能正在一个不返回 void 的函数中使用 ASSERT_*()ASSERT_*() 只能在 void 函数中使用,因为我们的构建系统禁用了异常。请在此处查看更多详细信息 here

我的死亡测试挂起(或段错误)。我该如何修复它?

在 GoogleTest 中,死亡测试在子进程中运行,它们的工作方式很微妙。要编写死亡测试,你真正需要了解它们的工作方式——请参阅断言参考中的 死亡断言中的详细信息。

特别是,死亡测试不喜欢父进程中有多个线程。因此,你可以尝试的第一件事是消除在 EXPECT_DEATH() 之外创建线程。例如,你可能想要在测试中使用模拟或伪对象而不是真实对象。

有时这是不可能的,因为你必须使用的某些库可能在甚至到达 main() 之前就创建了线程。在这种情况下,你可以尝试通过将尽可能多的活动移到 EXPECT_DEATH() 内部(在极端情况下,你想要将所有内容都移到内部),或在其中留下尽可能少的东西来尽量减少冲突的可能性。此外,你可以尝试将死亡测试样式设置为 "threadsafe",它更安全但更慢,看看是否有帮助。

如果你选择线程安全的死亡测试,请记住它们会在子进程中从头开始重新运行测试程序。因此,请确保你的程序可以与自身并排运行并且是确定性的。

最后,这归结为良好的并发编程。你必须确保你的程序中没有竞争条件或死锁。没有灵丹妙药 - 对不起!

我应该使用测试夹具的构造函数/析构函数还是 SetUp()/TearDown()?

首先要记住的是,GoogleTest 不会在多个测试中重用同一个测试夹具对象。对于每个 TEST_F,GoogleTest 将创建一个新的测试夹具对象,立即调用 SetUp(),运行测试主体,调用 TearDown(),然后删除测试夹具对象。

当你需要编写每个测试的设置和拆卸逻辑时,你可以选择使用测试夹具构造函数/析构函数或 SetUp()/TearDown()。前者通常是首选,因为它具有以下优点

在以下情况下,你可能仍然想要使用 SetUp()/TearDown()

当我使用 ASSERT_PRED* 时,编译器抱怨“no matching function to call”。我该如何修复它?

有关详细信息,请参阅断言参考中的 EXPECT_PRED*

当我调用 RUN_ALL_TESTS() 时,我的编译器抱怨“ignoring return value”。为什么?

有些人一直忽略 RUN_ALL_TESTS() 的返回值。也就是说,不是

  return RUN_ALL_TESTS();

他们写

  RUN_ALL_TESTS();

这是错误且危险的。测试服务需要查看 RUN_ALL_TESTS() 的返回值,以便确定测试是否通过。如果你的 main() 函数忽略它,即使它有 GoogleTest 断言失败,你的测试也会被认为是成功的。非常糟糕。

我们已决定解决此问题(感谢 Michael Chastain 的想法)。现在,当使用 gcc 编译时,你的代码将不再能够忽略 RUN_ALL_TESTS()。如果你这样做,你将收到一个编译器错误。

如果你看到编译器抱怨你忽略了 RUN_ALL_TESTS() 的返回值,则修复很简单:只需确保其值用作 main() 的返回值即可。

但是我们如何引入一个破坏现有测试的更改呢?好吧,在这种情况下,代码首先就坏了,所以我们没有破坏它。 :-)

我的编译器抱怨构造函数(或析构函数)无法返回值。发生了什么?

由于 C++ 的一个特性,为了支持将消息流式传输到 ASSERT_* 的语法,例如

  ASSERT_EQ(1, Foo()) << "blah blah" << foo;

我们不得不放弃在构造函数和析构函数中使用 ASSERT*FAIL*(但不是 EXPECT*ADD_FAILURE*)。解决方法是将构造函数/析构函数的内容移动到私有 void 成员函数,或者切换到 EXPECT_*()(如果有效)。用户指南中的这个部分对此进行了解释。

我的 SetUp() 函数未被调用。为什么?

C++ 区分大小写。你是否将其拼写为 Setup()

同样,有时人们将 SetUpTestSuite() 拼写为 SetupTestSuite(),并想知道为什么它从未被调用。

我有几个测试套件共享相同的测试夹具逻辑;我是否必须为每个测试套件定义一个新的测试夹具类?这似乎很乏味。

你不必这样做。而不是

class FooTest : public BaseTest {};

TEST_F(FooTest, Abc) { ... }
TEST_F(FooTest, Def) { ... }

class BarTest : public BaseTest {};

TEST_F(BarTest, Abc) { ... }
TEST_F(BarTest, Def) { ... }

你可以简单地 typedef 测试夹具

typedef BaseTest FooTest;

TEST_F(FooTest, Abc) { ... }
TEST_F(FooTest, Def) { ... }

typedef BaseTest BarTest;

TEST_F(BarTest, Abc) { ... }
TEST_F(BarTest, Def) { ... }

GoogleTest 的输出被埋没在一堆 LOG 信息中。我该怎么办?

GoogleTest 的输出应该是简洁且对人友好的报告。如果你的测试本身生成了文本输出,它将与 GoogleTest 的输出混合在一起,使得难以阅读。然而,有一个简单的解决方案。

由于 LOG 信息会输出到 stderr,我们决定让 GoogleTest 输出到 stdout。这样,你可以使用重定向轻松地将两者分开。例如:

$ ./my_test > gtest_output.txt

为什么我应该优先选择测试夹具而不是全局变量?

有几个很好的理由:

  1. 你的测试很可能需要更改全局变量的状态。这使得难以避免副作用从一个测试逃逸并污染其他测试,从而导致调试困难。通过使用夹具,每个测试都有一组全新的变量(但具有相同的名称)。因此,测试之间保持相互独立。
  2. 全局变量会污染全局命名空间。
  3. 测试夹具可以通过子类化进行重用,而全局变量则不容易做到这一点。如果许多测试套件都有共同之处,这将非常有用。

ASSERT_DEATH() 中的 statement 参数可以是哪些内容?

ASSERT_DEATH(statement, matcher)(或任何 death assertion 宏)可以在 statement 有效的任何地方使用。因此,基本上 statement 可以是当前上下文中任何有意义的 C++ 语句。特别是,它可以引用全局和/或局部变量,并且可以是:

以下是一些示例:

// A death test can be a simple function call.
TEST(MyDeathTest, FunctionCall) {
  ASSERT_DEATH(Xyz(5), "Xyz failed");
}

// Or a complex expression that references variables and functions.
TEST(MyDeathTest, ComplexExpression) {
  const bool c = Condition();
  ASSERT_DEATH((c ? Func1(0) : object2.Method("test")),
               "(Func1|Method) failed");
}

// Death assertions can be used anywhere in a function.  In
// particular, they can be inside a loop.
TEST(MyDeathTest, InsideLoop) {
  // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.
  for (int i = 0; i < 5; i++) {
    EXPECT_DEATH_M(Foo(i), "Foo has \\d+ errors",
                   ::testing::Message() << "where i is " << i);
  }
}

// A death assertion can contain a compound statement.
TEST(MyDeathTest, CompoundStatement) {
  // Verifies that at lease one of Bar(0), Bar(1), ..., and
  // Bar(4) dies.
  ASSERT_DEATH({
    for (int i = 0; i < 5; i++) {
      Bar(i);
    }
  },
  "Bar has \\d+ errors");
}

我有一个夹具类 FooTest,但 TEST_F(FooTest, Bar) 给我报错 "no matching function for call to `FooTest::FooTest()'"。为什么?

GoogleTest 需要能够创建你的测试夹具类的对象,因此它必须具有默认构造函数。通常编译器会为你定义一个。但是,在某些情况下你必须自己定义:

为什么 GoogleTest 要求整个测试套件(而不是单个测试)在使用 ASSERT_DEATH 时命名为 *DeathTest

GoogleTest 不会交错来自不同测试套件的测试。也就是说,它首先运行一个测试套件中的所有测试,然后运行下一个测试套件中的所有测试,依此类推。GoogleTest 这样做是因为它需要在运行测试套件中的第一个测试之前设置它,然后在之后将其拆除。拆分测试用例将需要多个设置和拆除过程,这是低效的并且使得语义不干净。

如果我们根据测试名称而不是测试用例名称来确定测试顺序,那么我们会遇到以下情况:

TEST_F(FooTest, AbcDeathTest) { ... }
TEST_F(FooTest, Uvw) { ... }

TEST_F(BarTest, DefDeathTest) { ... }
TEST_F(BarTest, Xyz) { ... }

由于 FooTest.AbcDeathTest 需要在 BarTest.Xyz 之前运行,并且我们不交错来自不同测试套件的测试,我们需要在运行 BarTest 用例中的任何测试之前运行 FooTest 用例中的所有测试。这与在 FooTest.Uvw 之前运行 BarTest.DefDeathTest 的要求相矛盾。

但是我不喜欢在我的整个测试套件中调用 *DeathTest,因为它同时包含 death 测试和非 death 测试。我该怎么办?

你不必这样做,但如果你喜欢,你可以将测试套件拆分为 FooTestFooDeathTest,其中的名称清楚地表明它们是相关的。

class FooTest : public ::testing::Test { ... };

TEST_F(FooTest, Abc) { ... }
TEST_F(FooTest, Def) { ... }

using FooDeathTest = FooTest;

TEST_F(FooDeathTest, Uvw) { ... EXPECT_DEATH(...) ... }
TEST_F(FooDeathTest, Xyz) { ... ASSERT_DEATH(...) ... }

GoogleTest 仅在 death 测试失败时才打印 death 测试子进程中的 LOG 信息。如何在 death 测试成功时看到 LOG 信息?

打印 EXPECT_DEATH() 中语句生成的 LOG 信息会使在父进程的日志中搜索真正的问题变得更加困难。因此,GoogleTest 仅在 death 测试失败时才打印它们。

如果你真的需要看到这些 LOG 信息,一种解决方法是暂时破坏 death 测试(例如,通过更改它期望匹配的正则表达式模式)。诚然,这是一种 hack。在实现 fork-and-exec 样式的 death 测试后,我们将考虑一个更永久的解决方案。

当我使用断言时,编译器抱怨 no match for 'operator<<'。这是怎么回事?

如果你在断言中使用用户定义的类型 FooType,你必须确保定义了一个 std::ostream& operator<<(std::ostream&, const FooType&) 函数,以便我们可以打印 FooType 的值。

此外,如果 FooType 在命名空间中声明,则 << 运算符也需要在相同的命名空间中定义。有关详细信息,请参阅 Tip of the Week #49

如何禁止 Windows 上的内存泄漏消息?

由于静态初始化的 GoogleTest 单例需要在堆上分配内存,因此 Visual C++ 内存泄漏检测器将在程序运行结束时报告内存泄漏。避免这种情况的最简单方法是使用 _CrtMemCheckpoint_CrtMemDumpAllObjectsSince 调用来不报告任何静态初始化的堆对象。有关更多详细信息和其他堆检查/调试例程,请参阅 MSDN。

我的代码如何检测它是否正在测试中运行?

如果你编写的代码会嗅探它是否正在测试中运行并相应地执行不同的操作,那么你正在将仅测试逻辑泄漏到生产代码中,并且没有简单的方法来确保仅测试代码路径不会在生产中被错误地运行。这种聪明也导致了 Heisenbugs。因此,我们强烈建议不要这样做,并且 GoogleTest 不提供执行此操作的方法。

一般来说,推荐的导致代码在测试下表现不同的方式是 依赖注入。你可以从测试和生产代码中注入不同的功能。由于你的生产代码根本不链接到用于测试的逻辑(BUILD 目标的 testonly 属性有助于确保这一点),因此没有意外运行它的危险。

但是,如果你真的真的真的别无选择,并且如果你遵循以 _test 结尾测试程序名称的规则,你可以使用嗅探可执行文件名(main() 中的 argv[0])的可怕的 hack 来了解代码是否正在测试中运行。

如何临时禁用测试?

如果你有一个无法立即修复的损坏的测试,你可以将 DISABLED_ 前缀添加到其名称中。这将使其从执行中排除。这比注释掉代码或使用 #if 0 更好,因为禁用的测试仍然会被编译(因此不会腐烂)。

要将禁用的测试包含在测试执行中,只需使用 --gtest_also_run_disabled_tests 标志调用测试程序。

如果我在不同的命名空间中定义了两个单独的 TEST(Foo, Bar) 测试方法,可以吗?

可以。

规则是同一测试套件中的所有测试方法必须使用相同的夹具类。这意味着以下内容是允许的,因为两个测试都使用相同的夹具类(::testing::Test)。

namespace foo {
TEST(CoolTest, DoSomething) {
  SUCCEED();
}
}  // namespace foo

namespace bar {
TEST(CoolTest, DoSomething) {
  SUCCEED();
}
}  // namespace bar

但是,以下代码是不允许的,并且会产生来自 GoogleTest 的运行时错误,因为测试方法正在使用具有相同测试套件名称的不同测试夹具类。

namespace foo {
class CoolTest : public ::testing::Test {};  // Fixture foo::CoolTest
TEST_F(CoolTest, DoSomething) {
  SUCCEED();
}
}  // namespace foo

namespace bar {
class CoolTest : public ::testing::Test {};  // Fixture: bar::CoolTest
TEST_F(CoolTest, DoSomething) {
  SUCCEED();
}
}  // namespace bar