GoogleTest Primer
入门
术语
googletest使用Test Case对相关测试进行分组,ISTQB使用Test Suite表示对相关测试进行分组。
googletest中的Test对应ISTQB中的Test Case
googletest最近开始用术语Test Case与Test Suite。首选 API 是TestSuite。旧的 TestCase API 正在慢慢被弃用和重构。
基本概念
一个test suite
包含一个或多个test
。你应该将test
分组到能够表示测试代码结构的test suite
中。当test suite
中的多个test
需要共享公共对象和子例程时,您可以将它们放入一个test fixture
类中。
一个测试程序可以包含多个test suite
。
We’ll now explain how to write a test program, starting at the individual assertion level and building up to tests and test suites.
我们现在将解释如何编写测试程序,从单个断言级别开始,然后构建测试和测试套件。
断言
googletest assertions are macros that resemble function calls. You test a class or function by making assertions about its behavior. When an assertion fails, googletest prints the assertion’s source file and line number location, along with a failure message. You may also supply a custom failure message which will be appended to googletest’s message.
The assertions come in pairs that test the same thing but have different effects on the current function. ASSERT_*
versions generate fatal failures when they fail, and abort the current function. EXPECT_*
versions generate nonfatal failures, which don’t abort the current function. Usually EXPECT_*
are preferred, as they allow more than one failure to be reported in a test. However, you should use ASSERT_*
if it doesn’t make sense to continue when the assertion in question fails.
Since a failed ASSERT_*
returns from the current function immediately, possibly skipping clean-up code that comes after it, it may cause a space leak. Depending on the nature of the leak, it may or may not be worth fixing - so keep this in mind if you get a heap checker error in addition to assertion errors.
To provide a custom failure message, simply stream it into the macro using the <<
operator or a sequence of such operators. See the following example, using the ASSERT_EQ
and EXPECT_EQ
macros to verify value equality:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
for (int i = 0; i < x.size(); ++i) {
EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
Anything that can be streamed to an ostream
can be streamed to an assertion macro–in particular, C strings and string
objects. If a wide string (wchar_t*
, TCHAR*
in UNICODE
mode on Windows, or std::wstring
) is streamed to an assertion, it will be translated to UTF-8 when printed.
GoogleTest provides a collection of assertions for verifying the behavior of your code in various ways. You can check Boolean conditions, compare values based on relational operators, verify string values, floating-point values, and much more. There are even assertions that enable you to verify more complex states by providing custom predicates. For the complete list of assertions provided by GoogleTest, see the Assertions Reference.
Simple Tests
To create a test:
-
使用
TEST()
宏来定义和命名测试函数。这些是没有返回值的普通 C++ 函数。Use the
TEST()
macro to define and name a test function. These are ordinary C++ functions that don’t return a value. -
在此函数中,连同您想要包含的任何有效 C++ 语句,使用各种 googletest 断言来检查值。
In this function, along with any valid C++ statements you want to include, use the various googletest assertions to check values.
-
测试的结果由断言决定;如果测试中的任何断言失败(致命或非致命),或者如果测试崩溃,则整个测试失败。否则,它成功。
The test’s result is determined by the assertions; if any assertion in the test fails (either fatally or non-fatally), or if the test crashes, the entire test fails. Otherwise, it succeeds.
// The first argument is the name of the test suite, and the second argument is the test’s name within the test suite.
// TestSuiteName:test suite name,测试套件的名字,可自定义
// TestName:test name,测试的名字,可自定义
TEST(TestSuiteName, TestName) {
... test body ...
}
TEST()
论点从一般到具体。第一个参数是测试套件的名称,第二个参数是测试套件中的测试名称。两个名称都必须是有效的 C++ 标识符,并且不应包含任何下划线 ( _
)。一个测试的全名由它包含的测试套件和它的个人名称组成。来自不同测试套件的测试可以具有相同的单独名称。
TEST()
arguments go from general to specific. The first argument is the name of the test suite, and the second argument is the test’s name within the test suite. Both names must be valid C++ identifiers, and they should not contain any underscores (_
). A test’s full name consists of its containing test suite and its individual name. Tests from different test suites can have the same individual name.
例如,让我们采用一个简单的整数函数
For example, let’s take a simple integer function:
int Factorial(int n); // Returns the factorial of n
此功能的测试套件可能如下所示:
A test suite for this function might look like:
// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
EXPECT_EQ(Factorial(0), 1);
}
// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
EXPECT_EQ(Factorial(1), 1);
EXPECT_EQ(Factorial(2), 2);
EXPECT_EQ(Factorial(3), 6);
EXPECT_EQ(Factorial(8), 40320);
}
googletest 按测试套件对测试结果进行分组,因此逻辑相关的测试应该在同一个测试套件中;换句话说,它们的第一个参数 TEST()
应该是相同的。在上面的示例中,我们有两个测试 HandlesZeroInput
和HandlesPositiveInput
,它们属于同一个测试套件FactorialTest
。
在命名测试套件和测试时,您应该遵循与命名函数和类相同的约定 。
可用性:Linux、Windows、Mac。
googletest groups the test results by test suites, so logically related tests should be in the same test suite; in other words, the first argument to their TEST()
should be the same. In the above example, we have two tests, HandlesZeroInput
and HandlesPositiveInput
, that belong to the same test suite FactorialTest
.
When naming your test suites and tests, you should follow the same convention as for naming functions and classes.
Availability: Linux, Windows, Mac.
Test Fixtures: Using the Same Data Configuration for Multiple Tests
如果你发现你需要写多个相似的测试操作,你可以使用test fixture
。它允许你为多个不同的测试重用相同的对象配置。
To create a fixture:
- 创建一个test fixture类,该类是
::testing::Test
的派生类。设置test fixture类的主体部分的权限为protected
因为我们希望通过子类访问fixture的成员。 - 在test fixture类中声明你计划使用的任何对象。
- 有必要的话,编写一个默认构造或
SetUp
函数去为每个测试准备这个对象。注意这里是SetUp
不是Setup
- 有必要的话,编写一个析构或
TearDown()
函数去释放你在SetUp()
中分配的资源。要了解什么时候该用构造/析构或什么时候该用SetUp()/TearDown()
,请查看FAQ。 - 如果需要的话,为你的测试定义要共享的子程序。
使用fixture时,使用TEST_F()
代替TEST()
,因为它允许你访问test fixture类中的对象和子例程。
// TestFixtureName:test suite name,不可自定义,其值为定义的test fixture类的名字
// Testname:test name,可自定义
TEST_F(TestFixtureName, TestName) {
... test body ...
}
类似于TEST()
,第一个参数TestFixtureName
表示测试套件名,但是这里的套件名必须是test fixture类的名字。TEST_F()
中的_F
表示的就是fixture的意思。
Unfortunately, the C++ macro system does not allow us to create a single macro that can handle both types of tests. Using the wrong macro causes a compiler error.不幸的是,C++ 宏系统不允许我们创建可以处理两种类型测试的单个宏。使用错误的宏会导致编译器错误。
在使用TEST_F()
之前,你必须首先定义一个test fixture类。否则,你将会收到编译错误virtual outside class declaration
。
For each test defined with TEST_F()
, googletest will create a fresh test fixture at runtime, immediately initialize it via SetUp()
, run the test, clean up by calling TearDown()
, and then delete the test fixture. Note that different tests in the same test suite have different test fixture objects, and googletest always deletes a test fixture before it creates the next one. googletest does not reuse the same test fixture for multiple tests. Any changes one test makes to the fixture do not affect other tests.对于用 定义的每个测试TEST_F()
,googletest 将在运行时创建一个新的测试夹具,立即通过 初始化它SetUp()
,运行测试,通过调用清理TearDown()
,然后删除测试夹具。请注意,同一测试套件中的不同测试具有不同的测试夹具对象,并且 googletest 总是在创建下一个测试夹具之前删除一个测试夹具。googletest并没有重用多个测试相同的测试夹具。一个测试对夹具所做的任何更改都不会影响其他测试。
As an example, let’s write tests for a FIFO queue class named Queue
, which has the following interface:
我们要为一个名为Queue的类写测试,该类具有以下接口:
As an example, let’s write tests for a FIFO queue class named Queue
, which has the following interface:
template <typename E> // E is the element type.
class Queue {
public:
Queue();
void Enqueue(const E& element);
E* Dequeue(); // Returns NULL if the queue is empty.
size_t size() const;
...
};
首先,顶一个一个test fixture类。通常,我们会将该类命名为FooTest
,其中Foo
表示你要测试的类。比如,这里我们要测试Queue
类,我们可以将test fixture类的名字命名为QueueTest
。
// 定义一个test fixture类,类名为QueueTest,该类继承自testing::Test
class QueueTest : public ::testing::Test {
protected:
void SetUp() override {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// void TearDown() override {}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
在这个例子中,我们不需要写TearDown()
函数,因为在析构函数运行完之后,没有需要我们清理的东西。
接下来,我们将使用TEST_F()
和这个test fixture类实现对Queue的测试。
TEST_F(QueueTest, IsEmptyInitially) {
EXPECT_EQ(q0_.size(), 0);
}
TEST_F(QueueTest, DequeueWorks) {
int* n = q0_.Dequeue();
EXPECT_EQ(n, nullptr);
n = q1_.Dequeue();
ASSERT_NE(n, nullptr);
EXPECT_EQ(*n, 1);
EXPECT_EQ(q1_.size(), 0);
delete n;
n = q2_.Dequeue();
ASSERT_NE(n, nullptr);
EXPECT_EQ(*n, 2);
EXPECT_EQ(q2_.size(), 1);
delete n;
}
The above uses both ASSERT_*
and EXPECT_*
assertions. The rule of thumb is to use EXPECT_*
when you want the test to continue to reveal more errors after the assertion failure, and use ASSERT_*
when continuing after failure doesn’t make sense. For example, the second assertion in the Dequeue
test is ASSERT_NE(n, nullptr)
, as we need to dereference the pointer n
later, which would lead to a segfault when n
is NULL
.
运行上面的测试代码,会发生下面的情况:
- googletest构造一个
QueueTest
对象(我们暂且称之为t1
)。 t1.SetUp()
初始化t1
。- 执行对t1的第一个测试(即
IsEmptyInitially
)。 - 测试结束,调用
t1.TearDown()
进行清理。(清理什么?) t1
被析构。- 在另一个
QueueTest
对象上重复上述步骤,这次运行DequeueWorks
测试。
Invoking the Tests(调用测试)
TEST()
和TEST_F()
隐式地向googletest注册它们的测试。因此,与许多其他的C++测试框架不同,你不必为了运行它们而重新列出所有定义的测试。
定义完测试之后,你可以通过运行函数RUN_ALL_TEST()
执行所有的测试。如果所有的测试均通过,该函数返回0,否则,返回1。要注意的是,RUN_ALL_TEST()
会执行你的链接单元中的所有测试,包括来自不同test suite
中的测试,甚至包括来自不同源文件中的测试。
When invoked, the RUN_ALL_TESTS()
macro:
当RUN_ALL_TESTS()
被调用是会发生下面的操作:
- 保存所有的googletest flags的状态
- 为第一个测试创建一个test fixture对象
- 通过
SetUp()
进行初始化 - 在test fixture对象上执行测试(对test fixture对象进行测试)
- 通过
TearDown()
清除test fixture对象数据(清除谁的,如何清除) - 删除test fixture对象
- 恢复所有的googletest flags的状态
- 对下一个测试重复上述步骤,直到所有测试都运行完毕。
如果其中某一步发生fatal failure
,将会跳过后续步骤。
注意:你不能忽略
RUN_ALL_TESTS()
的返回值,否则你将收到编译错误。这种设计的根本原因是自动化测试服务需要根据其退出代码而不是其stdout/stderr输出来确定测试是否通过;因此你的main()
函数必须返回RUN_ALL_TESTS()
的值.RUN_ALL_TESTS()
只可以被调用一次。多次调用它会与一些高级 googletest 功能(例如,线程安全 死亡测试)发生冲突, 因此不受支持。
Writing the main() Function(编写main函数)
我们推荐使用链接gtest_main
而不是用户自己写main函数,其定义了一个合适的切入点。有关详细信息,请参阅本节末尾。本节其余部分的内容针对的是以下情况:你需要在运行测试之前做一些不能够或者不适合利用test fixture 和 test suites完成的操作。
如果你使用了自己编写的main
函数,那么main
函数的返回值应该是RUN_ALL_TESTS()
的值。
你可以参考下面这个范例:
#include "this/package/foo.h"
#include "gtest/gtest.h"
namespace my {
namespace project {
namespace {
// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
protected:
// You can remove any or all of the following functions if their bodies would
// be empty.
FooTest() {
// You can do set-up work for each test here.
}
~FooTest() override {
// You can do clean-up work that doesn't throw exceptions here.
}
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
void SetUp() override {
// Code here will be called immediately after the constructor (right
// before each test).
}
void TearDown() override {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Class members declared here can be used by all tests in the test suite
// for Foo.
};
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
const std::string input_filepath = "this/package/testdata/myinputfile.dat";
const std::string output_filepath = "this/package/testdata/myoutputfile.dat";
Foo f;
EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0);
}
// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
// Exercises the Xyz feature of Foo.
}
} // namespace
} // namespace project
} // namespace my
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
::testing::InitGoogleTest()
函数会解析命令行中googletest定义的flags,并删除所有可识别的flag。这允许用户通过各种flag来控制测试程序的行为,我们将在 AdvancedGuide中介绍这些flags。必须在调用RUN_ALL_TEST()
函数之前调用::testing::InitGoogleTest()
函数,否则设定的flags将无法被正确初始化。
在 Windows 上,InitGoogleTest()
也适用于宽字符串(wide strings),因此它也可用于以UNICODE
模式编译的程序。
你是否觉得编写这些main
函数比较费劲?我们真是再同意不过了,这也就是为什么Google Test要提供一个main
函数的基本实现的原因。如果你恰好有这方面的需求,那么你只需要将你的测试与gtest_main
库链接到一起,就可以了。
NOTE:
ParseGUnitFlags()
is deprecated in favor ofInitGoogleTest()
.注意:
ParseGUnitFlags()
被弃用,取而代之的是InitGoogleTest()
。
已知限制
- Google Test 被设计为线程安全的。该实现在
pthreads
库可用的系统上是线程安全的。目前在其他系统(例如 Windows)上同时使用来自两个线程的 Google Test 断言是 不安全的。在大多数测试中,这不是问题,因为通常断言是在主线程中完成的。如果您想提供帮助,您可以自愿gtest-port.h
为您的平台实现必要的同步原语 。