因为AdvancedGuide文档太长,分上下两部分,本文档接googletest--AdvancedGuide(译文)上:Google C++单元测试框架GoogleTest---AdvancedGuide(译文)上。
一、在子程序中使用断言(Using Assertions in Sub-routines)
1.1 将跟踪添加到断言
如果从几个地方调用测试子程序,当其中的断言失败时,可能很难判断失败来自哪个子程序的调用。 您可以使用额外的日志或自定义失败消息缓解这个问题,但通常会堵塞您的测试。 更好的解决方案是使用SCOPED_TRACE宏:
SCOPED_TRACE(
message);
messsage可以是任何可以流入std::ostream的东西。此宏会将当前文件名、行号和给定消息添加到每个失败消息中。 当控件离开当前词法作用域时,效果将被撤消。
For example,
void Sub1(int n) {
EXPECT_EQ(1, Bar(n));
EXPECT_EQ(2, Bar(n + 1));
} TEST(FooTest, Bar) {
{
SCOPED_TRACE("A"); // This trace point will be included in
// every failure in this scope.
Sub1(1);
}//到这SCOPED_TRACE的作用域就结束。
// Now it won't.
Sub1(9);//都调用了子程序Sub1
}
可能会导致这样的消息:
path/to/foo_test.cc:11: Failure
Value of: Bar(n)
Expected: 1
Actual: 2
Trace:
path/to/foo_test.cc:17: A path/to/foo_test.cc:12: Failure
Value of: Bar(n + 1)
Expected: 2
Actual: 3
没有跟踪,很难知道两个失败分别来自哪个Sub1()的调用。 (你可以在Sub1()中为每个断言添加一个额外的消息,以指示n的值,但这很乏味。)
关于使用SCOPED_TRACE的一些提示:
没有跟踪,很难知道两个失败分别来自哪个Sub1()的调用。 (你可以在Sub1()中为每个断言添加一个额外的消息,以指示n的值,但这很乏味。)
关于使用SCOPED_TRACE的一些提示:
- 使用合适的消息,通常足以在子例程的开头使用SCOPED_TRACE,而不是在每个调用站点。
- 当调用循环内的子例程时,使循环迭代器成为SCOPED_TRACE中的消息的一部分,以便您可以知道失败来自哪个迭代。
- 有时,跟踪点的行号足以识别子例程的特定调用。在这种情况下,您不必为SCOPED_TRACE选择唯一的消息。你可以简单地使用“”。
- 当外部作用域中有一个SCOPED_TRACE时,可以在内部作用域中使用SCOPED_TRACE。在这种情况下,所有活动跟踪点将按照遇到的相反顺序包含在失败消息中。
- 跟踪转储是可以在Emacs的编译缓冲区中点击 - 命中返回行号,你会被带到源文件中的那一行!
1.2 传播致命失败
使用ASSERT_ *和FAIL *时的常见陷阱认为当它们失败时,它们会中止整个测试。 例如,以下测试将会导致错误:
void Subroutine() {
// Generates a fatal failure and aborts the current function.
ASSERT_EQ(1, 2);//失败时只是终止当前函数
// The following won't be executed.
...
} TEST(FooTest, Bar) {
Subroutine();
// The intended behavior is for the fatal failure
// in Subroutine() to abort the entire test.
// The actual behavior: the function goes on after Subroutine() returns.
// 实际行为:函数在Subroutine()返回后继续。
int* p = NULL;
*p = 3; // Segfault!产生一个错误
}
由于我们不使用异常,因此在技术上不可能在这里实现预期的行为。 为了减轻这种情况,Google测试提供了两种解决方案。 您可以使用(ASSERT | EXPECT)_NO_FATAL_FAILURE断言或HasFatalFailure()函数。 它们在以下两个小节中描述。
1.3在子程序中的断言(Asserting on Subroutines)
如上所示,如果你的测试调用了一个有ASSERT_ *失败的子程序,测试将在子程序返回后继续。 这可能不是你想要的。
通常人们希望致命的失败传播像异常一样。 为此,Google测试提供以下宏:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_NO_FATAL_FAILURE( statement);
|
EXPECT_NO_FATAL_FAILURE( statement);
|
statement doesn't generate any new fatal failures in the current thread. |
仅检查执行断言的线程中的失败,以确定这种类型的断言的结果。 如果语句创建新线程,这些线程中的失败将被忽略。
例如:
ASSERT_NO_FATAL_FAILURE(Foo()); int i;
EXPECT_NO_FATAL_FAILURE({
i = Bar();
});
1.4检查当前测试中的故障
:: test :: Test类中的HasFatalFailure(): 如果当前测试中的断言遭遇致命故障,则返回true。 这允许函数捕获子例程中的致命故障并及早返回。
class Test {
public:
...
static bool HasFatalFailure();
};
典型的用法,基本上模拟抛出的异常的行为是:
TEST(FooTest, Bar) {
Subroutine(); // Aborts if Subroutine() had a fatal failure.
if (HasFatalFailure())
return;
// The following won't be executed.
...
}
HasFatalFailure如果在TEST(),TEST_F()或测试夹具之外使用,则必须添加:: testing :: Test ::前缀,如:
if (::testing::Test::HasFatalFailure())
return;
类似的,HasNonfatalFailure():如果当前测试至少有一个非致命失败,返回true。
HasFailure()
:如果当前测试至少有一个失败,返回true。
二、记录其他信息Logging Additional Information
在测试代码中,可以调用RecordProperty(“key”,value)来记录附加信息,其中value可以是字符串或int。 如果指定一个键,则为键记录的最后一个值将发送到XML输出。
例如
TEST_F(WidgetUsageTest, MinAndMaxWidgets) {
RecordProperty("MaximumWidgets", ComputeMaxUsage());
RecordProperty("MinimumWidgets", ComputeMinUsage());
}
will output XML like this:
...
<testcase name="MinAndMaxWidgets" status="run" time="6" classname="WidgetUsageTest"
MaximumWidgets="12"
MinimumWidgets="9" />
...
注意:
- RecordProperty()是Test类的静态成员。 因此,如果在TEST体和测试夹具类之外使用,则需要使用前缀:: testing :: Test ::。
- 键必须是有效的XML属性名称,且不能与Google Test(名称,状态,时间,类名,类型_参数和值_参数)已使用的键冲突。
- 允许在测试的生命周期之外调用RecordProperty()。 如果它在测试之外调用,但在测试用例的SetUpTestCase()和TearDownTestCase()方法之间调用,它将被归因于测试用例的XML元素。 如果在所有测试用例之外调用(例如在测试环境中),它将被归因于*XML元素。
三、在同一测试用例中的测试之间共享资源
Google Test为每个测试创建一个新的测试夹具对象,以使测试独立,更容易调试。 然而,有时测试使用昂贵的资源设置,使得单拷贝测试模型过于昂贵。
如果测试不更改资源,则它们在共享单个资源副本中没有任何危害。 因此,除了每次测试的set-up/tear-down,Google测试还支持每个测试用例的set-up/tear-down。 使用它:
- 在你的测试夹具类(比如FooTest)中,定义一些成员变量来保存共享资源。
- 在同一个测试夹具类中,定义一个静态void SetUpTestCase()函数(记住不要拼写它作为一个小u的SetupTestCase)来设置共享资源和静态void TearDownTestCase()函数来删除它们。
OK! 在运行FooTest测试用例中的第一个测试(即在创建第一个FooTest对象之前)之前,Google Test自动调用SetUpTestCase(),并在运行最后一个测试之后(即删除最后一个FooTest对象后)调用TearDownTestCase()。 在其间,测试可以使用共享资源。
记住测试顺序是未定义的,所以你的代码不能依赖于另一个之前或之后的测试。 此外,测试必须不能修改任何共享资源的状态,或者,如果它们修改状态,则它们必须在将控制传递给下一个测试之前将状态恢复到其原始值。
Here's an example of per-test-case set-up and tear-down:
class FooTest : public ::testing::Test {
protected:
// Per-test-case set-up.
// Called before the first test in this test case.
// Can be omitted if not needed.
static void SetUpTestCase() {
shared_resource_ = new ...;
} // Per-test-case tear-down.
// Called after the last test in this test case.
// Can be omitted if not needed.
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 ...
}
四、Global Set-Up and Tear-Down
正如你可以在测试级别和测试用例级别设置和拆卸,您也可以在测试程序级别执行。
首先,你要继承:: testing :: Environment类来定义一个测试环境:
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() {}
};
然后,通过调用:: testing :: Add Global Test Environment()函数,注册我们的环境类的实例:
Environment* AddGlobalTestEnvironment(Environment* env);
现在,当调用RUN_ALL_TESTS()时,它首先调用环境对象的SetUp()方法,然后如果没有致命失败则运行测试,最后调用环境对象的TearDown()。
注册多个环境对象是可以的。 在这种情况下,他们的SetUp()将按照它们注册的顺序被调用,并且它们的TearDown()将以相反的顺序被调用。
请注意,Google测试会对注册的环境对象拥有所有权。 因此,不要自己删除它们。
您应该在调用RUN_ALL_TESTS()之前调用AddGlobalTestEnvironment(),可能在main()中调用。 如果你使用gtest_main,你需要在main()启动之前调用它才能生效。 一种方法是定义一个全局变量,如下所示:
::testing::Environment* const foo_env = ::testing::AddGlobalTestEnvironment(new FooEnvironment);
但是,我们强烈建议您编写自己的main()并调用AddGlobalTestEnvironment(),因为依赖于全局变量的初始化使代码更难读取,并且可能会导致问题,当您从不同的转换单元注册多个环境, 它们之间的依赖性(记住编译器不保证来自不同转换单元的全局变量的初始化顺序)。
五、值参数化测试
//这个功能挺复杂,专门写在一个单独的文档解析:
http://www.cnblogs.com/jycboy/p/6118073.html
六、类型测试
假设您有一个接口的多个实现,并希望确保所有这些都满足一些常见的要求。 或者,您可能定义了几个类型,它们应该符合相同的“概念”,并且您想要验证它。 在这两种情况下,您都希望为不同类型重复相同的测试逻辑。
虽然你可以为你想测试的每个类型写一个TEST或TEST_F(你甚至可以把测试逻辑放入你从TEST调用的函数模板),它是乏味的,不缩放:如果你想要m个测试 n类型,你最终会写m * n TESTs。
类型测试允许您在类型列表上重复相同的测试逻辑。 你只需要写一次测试逻辑,虽然在写类型测试时你必须知道类型列表。 以下是您的操作方法:
1. 定义一个fixture类模板。 它应该由一个类型参数化。 记住继承:: testing :: Test:
template <typename T>
class FooTest : public ::testing::Test {
public:
...
typedef std::list<T> List;
static T shared_;
T value_;
};
2. 将类型列表与测试用例相关联,这将针对列表中的每个类型重复:
typedef ::testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_CASE(FooTest, MyTypes);
typedef对于TYPED_TEST_CASE宏正确解析是必要的。 否则编译器会认为类型列表中的每个逗号引入一个新的宏参数。
3. 使用TYPED_TEST()而不是TEST_F()为此测试用例定义一个类型测试。 您可以根据需要重复此操作次数:
TYPED_TEST(FooTest, DoesBlah) {
// Inside a test, refer to the special name TypeParam to get the type
// parameter. Since we are inside a derived class template, C++ requires
// us to visit the members of FooTest via 'this'.
TypeParam n = this->value_; // To visit static members of the fixture, add the 'TestFixture::'
// prefix.
n += TestFixture::shared_; // To refer to typedefs in the fixture, add the 'typename TestFixture::'
// prefix. The 'typename' is required to satisfy the compiler.
typename TestFixture::List values;
values.push_back(n);
...
} TYPED_TEST(FooTest, HasPropertyA) { ... }
You can see samples/sample6_unittest.cc
for a complete example.
七、类型参数化测试
在不知道类型参数的情况下编写测试--- 这就是“类型参数化测试”。
类型参数化测试类似于类型测试,除了它们不需要预知类型列表。 相反,您可以首先定义测试逻辑,然后使用不同类型列表实例化它。 你甚至可以在同一个程序中多次实例化它。
如果您正在设计一个接口或概念,则可以定义一组类型参数化测试,以验证接口/概念的任何有效实现应具有的属性。 然后,每个实现的作者可以使用他的类型来实例化测试套件,以验证它符合需求,而不必重复地编写类似的测试。 这里有一个例子:
1. 定义一个fixture类模板,就像我们用类型测试一样:
First, define a fixture class template, as we did with typed tests: template <typename T>
class FooTest : public ::testing::Test {
...
};
2. 声明你要定义的类型参数化测试用例:
TYPED_TEST_CASE_P(FooTest);
这个后缀P代表参数化或模式,随你怎么想。
3. 使用TYPED_TEST_P()来定义类型参数化测试。您可以根据需要重复多次:
TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0;
...
} TYPED_TEST_P(FooTest, HasPropertyA) { ... }
现在棘手的部分:您需要使用REGISTER_TYPED_TEST_CASE_P宏注册所有测试模式,然后才能实例化它们。
宏的第一个参数是测试用例名称; 其余的是在这个测试用例中的测试的名称:
REGISTER_TYPED_TEST_CASE_P(FooTest,
DoesBlah, HasPropertyA);
最后,你可以用你想要的类型来实例化模式。 如果你把上面的代码放在头文件中,你可以在多个C ++源文件#include它,并实例化多次。
typedef ::testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
为了区分模式的不同实例,INSTANTIATE_TYPED_TEST_CASE_P宏的第一个参数是将添加到实际测试用例名称中的前缀。 请记住为不同实例选择唯一前缀。
在类型列表只包含一个类型的特殊情况下,您可以直接编写该类型,而不使用:: testing :: Types <...>,如下所示:
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);
ou can see samples/sample6_unittest.cc
for a complete example.
**类型测试与类型参数化的区别:
1. 类型测试是你知道所有的类型,把所有类型注册,实例化测试。
2. 类型参数化是你是接口的定义者,知道每个接口函数的基本功能,而它的具体实现可以在将来被 人实现,你可以提前定义好测试用例,然后把所有的测试注册。
八、测试private修饰的代码
如果您更改软件的内部实现,只要用户不能观察到这种变化,您的测试就不会中断。 因此,根据黑盒测试原则,大多数时候你应该通过其公共接口测试你的代码。
如果你仍然发现自己需要测试内部实现代码,考虑是否有一个更好的设计,不需要你这样做。 如果你必须测试非公共接口代码。 有两种情况需要考虑:
- 静态函数(不同于静态成员函数!)或未命名的命名空间
- 私人或受保护的类成员
1. 静态函数
未命名的命名空间中的静态函数和定义/声明仅在同一转变单元中可见。要测试它们,您可以在你的* _test.cc文件中include 要测试的整个.cc文件。 (#include .cc文件不是重用代码的好方法 - 你不应该在生产代码中这样做!)
然而,更好的方法是将私有代码移动到foo :: internal命名空间中,其中foo是你项目通常使用的命名空间,并将私有声明放在* -internal.h文件中。允许您的生产.cc文件和测试包括此内部标头,但是您的客户端不包括。这样,您可以完全测试您的内部实现,而不会泄漏到您的客户端。
2. 私有类成员
私人class成员只能从班class或友元类那里接触。要访问类的私有成员,可以将测试夹具声明为类的朋友,并在夹具中定义访问器。使用夹具的测试,可以通过夹具中的访问器访问您的生产类的私有成员。注意,即使你的夹具是你的生产类的朋友,你的测试不是它的友元类,因为他们技术上是定义在夹具的子类中。
另一种测试私有成员的方法是将它们重构为一个实现类,然后在* -internal.h文件中声明它。您的客户端不允许包括此标题,但您的测试可以。这种称为Pimpl(私有实现)习语。
或者,您可以通过在类主体中添加以下行,将个别测试声明为class的友元类:
FRIEND_TEST(TestCaseName, TestName);
For example,
// foo.h
#include "gtest/gtest_prod.h" // Defines FRIEND_TEST.
class Foo {
...
private:
FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
int Bar(void* x);
}; // foo_test.cc
...
TEST(FooTest, BarReturnsZeroOnNull) {
Foo foo;
EXPECT_EQ(0, foo.Bar(NULL));
// Uses Foo's private member Bar().
}
当你的类在命名空间中定义时,需要特别注意,因为你应该在同一个命名空间中定义你的测试夹具和测试,如果你想他们是你的class的友元。 例如,如果要测试的代码如下所示:
namespace my_namespace { class Foo {
friend class FooTest;
FRIEND_TEST(FooTest, Bar);
FRIEND_TEST(FooTest, Baz);
...
definition of the class Foo
...
}; } // namespace my_namespace
Your test code should be something like:
namespace my_namespace {
class FooTest : public ::testing::Test {
protected:
...
}; TEST_F(FooTest, Bar) { ... }
TEST_F(FooTest, Baz) { ... } } // namespace my_namespace
九、Catching Failures
如果您要在Google测试之上构建测试实用程序,则需要测试您的实用程序。 你将使用什么框架来测试它? Google测试,当然。
挑战是验证您的测试实用程序是否正确报告故障。 在框架中通过抛出异常报告失败,您可以捕获异常并断言。 但是Google Test不会使用异常,那么我们如何测试一段代码是否会产生预期的失败呢?
“gtest / gtest-spi.h”包含一些构造来做到这一点。 #include 它,然后可以使用
EXPECT_FATAL_FAILURE(statement, substring);
to assert that statement generates a fatal (e.g. ASSERT_*
) failure whose message contains the given substring, or use
EXPECT_NONFATAL_FAILURE(statement, substring);
if you are expecting a non-fatal (e.g. EXPECT_*
) failure.
。。。。。//需要时再看
十、Getting the Current Test's Name
有时一个函数可能需要知道当前运行的测试的名称。 例如,您可以使用测试夹具的SetUp()方法根据正在运行的测试设置黄金文件名。 :: testing :: TestInfo类具有以下信息:
namespace testing { class TestInfo {
public:
// Returns the test case name and the test name, respectively.
//
// Do NOT delete or free the return value - it's managed by the
// TestInfo class.
const char* test_case_name() const;
const char* name() const;
}; } // namespace testing
To obtain a TestInfo
object for the currently running test, call current_test_info()
on the UnitTest
singleton object:
// Gets information about the currently running test.
// Do NOT delete the returned object - it's managed by the UnitTest class.
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
printf("We are in test %s of test case %s.\n",
test_info->name(), test_info->test_case_name());
如果没有运行测试,current_test_info()返回一个空指针。 特别是,你不能在TestCaseSetUp(),TestCaseTearDown()中找到测试用例名称(在那里你知道测试用例名称),或者从它们调用的函数。
十 一、运行测试程序:高级选项
要查看支持的标志及其用法的列表,请使用--help标志运行测试程序。 您也可以使用-h, - ?或/? 简称。 此功能在版本1.3.0中添加。
设置标志的三种方式:以--gtest_filter 为例
int main(int argc, char** argv) {
// Disables elapsed time by default.
::testing::GTEST_FLAG(print_time) = false; // This allows the user to override the flag on the command line.
::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();
}
1. 选择测试
此部分显示用于选择哪些测试运行的各种选项。
有时,在运行程序之前,必须列出程序中的可用测试,以便在需要时应用过滤器。 包括标志--gtest_list_tests覆盖所有其他标志,并列出以下格式的测试:
TestCase1.
TestName1
TestName2
TestCase2.
TestName
如果提供了标志,则列出的所有测试都不会真正运行。 此标志没有相应的环境变量。
1.2运行测试的子集
默认情况下,Google测试程序运行用户定义的所有测试。 有时,您只想运行测试的一个子集(例如,用于调试或快速验证更改)。 如果将GTEST_FILTER环境变量或--gtest_filter标志设置为过滤器字符串,则Google Test将仅运行其全名(以TestCaseName.TestName的形式)与过滤器匹配的测试。
过滤器的格式是通配符模式(称为正模式)的':'分隔的列表,可选地后跟一个“ - ”和另一个“:”分隔的模式列表(称为负模式)。 测试匹配过滤器当且仅当它与任何正模式匹配但不匹配任何负模式时。
模式可能包含“*”(匹配任何字符串)或'?' (匹配任何单个字符)。 为了方便,过滤器'* -NegativePatterns'也可以写为'-NegativePatterns'。
For example:
-
./foo_test
Has no flag, and thus runs all its tests. -
./foo_test --gtest_filter=*
Also runs everything, due to the single match-everything*
value. -
./foo_test --gtest_filter=FooTest.*
Runs everything in test caseFooTest
. -
./foo_test --gtest_filter=*Null*:*Constructor*
Runs any test whose full name contains either"Null"
or"Constructor"
. -
./foo_test --gtest_filter=-*DeathTest.*
Runs all non-death tests. -
./foo_test --gtest_filter=FooTest.*-FooTest.Bar
Runs everything in test caseFooTest
exceptFooTest.Bar
.
1.3 暂时禁用测试
如果你有一个失败的测试,无法立即修复,可以在其名称中添加DISABLED_前缀。 将排除它执行。 这比注释掉代码或使用#if 0更好,因为禁用的测试仍然编译(因此不会rot)。
如果你需要禁用测试用例中的所有测试,可以在每个测试名称的前面添加DISABLED_,或者将其添加到测试用例名称的前面。
例如,以下测试不会由Google Test运行,即使它们仍然将被编译:
// Tests that Foo does Abc.
TEST(FooTest, DISABLED_DoesAbc) { ... } class DISABLED_BarTest : public ::testing::Test { ... }; // Tests that Bar does Xyz.
TEST_F(DISABLED_BarTest, DoesXyz) { ... }
注意:此功能只能用于临时缓解。 您仍然必须在以后修复已禁用的测试。 提醒您,如果测试计划包含任何已停用的测试,Google测试会打印一条横幅警告您。
提示:您可以轻松计算已使用grep禁用的测试的数量。 此数字可用作提高测试质量的指标。
1.4 暂时启用禁用测试
要在测试执行中包括禁用的测试,只需使用--gtest_also_run_disabled_tests标志调用测试程序或将GTEST_ALSO_RUN_DISABLED_TESTS环境变量设置为0以外的值。您可以将此与--gtest_filter标志结合,以进一步选择要运行哪些已禁用的测试 。
代码中设置如下:前边加FLAGS_ (好坑啊,文档里也没说,我查出来的)。
testing::FLAGS_gtest_also_run_disabled_tests = 2;
2. 重复测试
有一段时间,你会碰到一个测试,它的结果是命中或错过。 也许它只会失败1%的时间,使它很难重现调试器下的错误。 这可能是挫折的主要根源。
--gtest_repeat标志允许您在程序中重复所有(或选定的)测试方法多次。 希望,一个脆弱的测试最终会失败,并给你一个机会进行调试。 以下是使用方法:
$ foo_test --gtest_repeat=1000 |
Repeat foo_test 1000 times and don't stop at failures. |
---|---|
$ foo_test --gtest_repeat=-1 |
A negative count means repeating forever. |
$ foo_test --gtest_repeat=1000 --gtest_break_on_failure |
Repeat foo_test 1000 times, stopping at the first failure. This is especially useful when running under a debugger: when the testfails, it will drop into the debugger and you can then inspect variables and stacks. |
$ foo_test --gtest_repeat=1000 --gtest_filter=FooBar |
Repeat the tests whose name matches the filter 1000 times. |
如果您的测试程序包含使用AddGlobalTestEnvironment()注册的全局set-up/tear-down代码,则在每次迭代中都会重复该测试程序,因为其中可能存在薄片。 您还可以通过设置GTEST_REPEAT环境变量来指定重复计数。
3.Shuffling the Tests洗牌测试
您可以指定--gtest_shuffle标志(或将GTEST_SHUFFLE环境变量设置为1),以便以随机顺序在程序中运行测试。 这有助于揭示测试之间的依赖关系。
默认情况下,Google测试使用根据当前时间计算的随机种子。 因此,你每次都会得到不同的顺序。 控制台输出包括随机种子值,以便以后可以重现与顺序相关的测试失败。 要明确指定随机种子,请使用--gtest_random_seed = SEED标志(或设置GTEST_RANDOM_SEED环境变量),其中SEED是介于0和99999之间的整数。种子值0是特殊的:它告诉Google Test执行默认行为 从当前时间计算种子。
如果将此与--gtest_repeat = N结合使用,Google测试会选择不同的随机种子,并在每次迭代中重新洗牌测试。
4.控制测试输出
本节教导如何调整测试结果的报告方式。
4.1彩色终端输出
Google测试可以在其终端输出中使用颜色,以便更容易地发现测试之间的分离,以及测试是否通过。
您可以设置GTEST_COLOR环境变量,或将--gtest_color命令行标志设置为yes,no或auto(默认值)以启用颜色,禁用颜色或让Google测试决定。 当值为auto时,如果且仅当输出发送到终端,并且(在非Windows平台上)TERM环境变量设置为xterm或xterm-color,Google Test将使用颜色。
4.2 抑制所用时间
默认情况下,Google测试会打印运行每个测试所需的时间。为了抑制这种情况,使用--gtest_print_time = 0命令行标志运行测试程序。将GTEST_PRINT_TIME环境变量设置为0具有相同的效果。
可用性:Linux,Windows,Mac。 (在Google Test 1.3.0及更低版本中,默认行为是不打印已用时间。)
4.3 生成XML报告
除了正常的文本输出之外,Google Test还可以向文件发送详细的XML报告。报告包含每个测试的持续时间,因此可以帮助您识别慢速测试。
要生成XML报告,请将GTEST_OUTPUT环境变量或--gtest_output标志设置为字符串“xml:_path_to_output_file_”,这将在给定位置创建文件。您也可以只使用字符串“xml”,在这种情况下,输出可以在当前目录中的test_detail.xml文件中找到。
如果指定目录(例如,Linux上的“xml:output / directory /”或Windows上的“xml:output \ directory \”),Google Test将在该目录中创建一个XML文件(测试程序foo_test或foo_test.exe的foo_test.xml)。如果文件已存在(可能是上次运行时遗留的文件),Google测试会选择不同的名称(例如foo_test_1.xml),以避免覆盖它。
报告使用此处描述的格式。它基于junitreport Ant任务,可以通过流行的连续构建系统(如Hudson)进行解析。由于该格式最初是用于Java,需要稍作解释才能将其应用于Google Test测试,如下所示:
<testsuites name="AllTests" ...>
<testsuite name="test_case_name" ...>
<testcase name="test_name" ...>
<failure message="..."/>
<failure message="..."/>
<failure message="..."/>
</testcase>
</testsuite>
</testsuites>
- <testsuites>根元素对应于整个测试程序。
-
<testsuite>
元素对应与test case. -
<testcase>
元素对应于Google Test测试函数。
For instance, the following program
TEST(MathTest, Addition) { ... }
TEST(MathTest, Subtraction) { ... }
TEST(LogicTest, NonContradiction) { ... }
could generate this report:
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="3" failures="1" errors="0" time="35" name="AllTests">
<testsuite name="MathTest" tests="2" failures="1" errors="0" time="15">
<testcase name="Addition" status="run" time="7" classname="">
<failure message="Value of: add(1, 1)
Actual: 3
Expected: 2" type=""/>
<failure message="Value of: add(1, -1)
Actual: 1
Expected: 0" type=""/>
</testcase>
<testcase name="Subtraction" status="run" time="5" classname="">
</testcase>
</testsuite>
<testsuite name="LogicTest" tests="1" failures="0" errors="0" time="5">
<testcase name="NonContradiction" status="run" time="5" classname="">
</testcase>
</testsuite>
</testsuites>
注意事项:
- <testuites>元素的tests属性包含多少个测试,而failures属性告诉它们有多少个失败。
<testcase>元素的属性:该测试用例中包含多少个测试; - time属性以毫秒表示测试、测试用例或整个测试程序的持续时间。
- 每个<failure>元素对应于单个失败的Google Test断言。
- 某些JUnit概念不适用于Google测试,但我们必须符合DTD。 因此,您将在报告中看到一些虚拟元素和属性。 您可以安全地忽略这些部分。
5.控制如何报告失败
5.1 将断言失败转换为断点
当在调试器下运行测试程序时,如果调试器可以捕获断言故障并自动进入交互模式,则非常方便。 Google测试的break-on-failure 模式支持此行为。
要启用它,请将GTEST_BREAK_ON_FAILURE环境变量设置为0以外的值。或者,您可以使用--gtest_break_on_failure命令行标志。
可用性:Linux,Windows,Mac。
5.2 禁用捕获测试 - 抛出异常Disabling Catching Test-Thrown Exceptions
Google测试可以在启用或不启用exception的情况下使用。如果测试抛出C ++异常或(在Windows上)结构化异常(SEH),默认情况下Google测试会捕获它,将其报告为测试失败,并继续执行下一个测试方法。这将最大化测试运行的覆盖率。此外,在Windows上,未捕获的异常将导致弹出窗口,因此捕获异常允许您自动运行测试。
然而,当调试测试失败时,您可能希望异常由调试器处理,以便您可以在抛出异常时检查调用堆栈。要实现此目的,请在运行测试时将GTEST_CATCH_EXCEPTIONS环境变量设置为0,或使用--gtest_catch_exceptions = 0标志。
5.3 让另一个测试框架驱动
如果您使用的项目已经使用了另一个测试框架,但尚未准备好完全切换到Google测试,则可以通过在现有测试中使用其断言来获得Google测试的许多优势。 只是改变你的main()函数看起来像:
#include "gtest/gtest.h" int main(int argc, char** argv) {
::testing::GTEST_FLAG(throw_on_failure) = true;
// Important: Google Test must be initialized.
::testing::InitGoogleTest(&argc, argv); ... whatever your existing testing framework requires ...
}
这样,除了你的框架提供的断言之外,您还可以使用Google Test断言,例如:
void TestFooDoesBar() {
Foo foo;
EXPECT_LE(foo.Bar(1), 100); // A Google Test assertion.
CPPUNIT_ASSERT(foo.IsEmpty()); // A native assertion.
}
。。。。。后边的省略了,这个应该需要不到
6. 将测试功能分发到多台机器
如果您有多个机器可以用来运行测试程序,您可能希望并行运行测试功能并更快地获得结果。我们称之为技术分片,其中每台机器被称为分片。
Google测试与测试分片兼容。要利用此功能,您的测试运行器(不是Google测试的一部分)需要执行以下操作:
- 分配多个计算机(分片)以运行测试。
- 在每个分片上,将GTEST_TOTAL_SHARDS环境变量设置为分片的总数。它对所有分片必须相同。
- 在每个分片上,将GTEST_SHARD_INDEX环境变量设置为分片的索引。不同的分片必须分配不同的索引,这些索引必须在[0,GTEST_TOTAL_SHARDS - 1]的范围内。
- 在所有分片上运行相同的测试程序。当Google测试看到上述两个环境变量时,它将选择要运行的测试函数的子集。在所有分片中,程序中的每个测试函数将只运行一次。
- 等待所有分片完成,然后收集并报告结果。
您的项目可能有没有Google Test的测试,因此不了解此协议。为了让测试运行器确定哪个测试支持分片,它可以将环境变量GTEST_SHARD_STATUS_FILE设置为不存在的文件路径。如果测试程序支持分片,它将创建此文件以确认事实(文件的实际内容此时不重要;虽然我们将来可能会在其中粘贴一些有用的信息。否则它不会创建它。
这里有一个例子来说明。假设您有一个包含以下5个测试函数的测试程序foo_test:
TEST(A, V)
TEST(A, W)
TEST(B, X)
TEST(B, Y)
TEST(B, Z)
你有3台机器在您的处置。 要并行运行测试功能,您需要在所有机器上将GTEST_TOTAL_SHARDS设置为3,并将GTEST_SHARD_INDEX分别设置为0,1和2。 然后你将在每台机器上运行相同的foo_test。
Google测试有权更改工作在分片上的分布情况,但这里有一种可能的情况:
- Machine #0 runs
A.V
andB.X
. - Machine #1 runs
A.W
andB.Y
. - Machine #2 runs
B.Z
.
十二、融合Google测试源文件
Google测试的实现包括〜30个文件(不包括自己的测试)。 有时你可能希望它们被打包在两个文件(.h和.cc),以便你可以轻松地将它们复制到一个新的机器,并开始黑客。 为此,我们在scripts /目录中提供了一个实验性Python脚本fuse_gtest_files.py(自1.3.0版本开始)。 假设你在你的机器上安装了Python 2.4或更高版本,只需去那个目录并运行
python fuse_gtest_files.py OUTPUT_DIR
并且您应该看到一个OUTPUT_DIR目录正在创建文件gtest / gtest.h和gtest / gtest-all.cc。 这些文件包含您使用Google测试所需的一切。 只需将它们复制到任何你想要的,你准备好写测试。 您可以使用scripts / test / Makefile文件作为如何编译针对它们的测试的示例。