本篇主要介绍Google Test 的测试示例。
TEST()宏
TEST()参数
TEST(TestSuiteName, TestName) {
... test body ...
}
TEST()
的参数按从一般到具体进行设置,第一个参数是Test Case 的名称,第二个参数是(隶属于第一个Test Case参数的)Test 的名称。这两个名字都必须是有效的 C++ 标识符,且它们都不应该包含下划线(_
)。一个测试的完整名称包括 Test Case 名称及 Test 的名称,不同 Test Case 的 Test 名称可以相同。
当命名你的测试套件和测试时,你应该遵循命名函数和类相同的规则。
googletest 根据 Test Case对测试结果进行分组,所以一些逻辑上相关的 Test应当放入同一个 Test Case中。换句话说,它们的 TEST() 的第一个参数应该是相同的。
TEST()测试示例
创建一个测试的步骤:
- 使用 TEST() 宏定义并命名一个测试函数。这些是普通的没有返回值的 C++ 函数。
- 在这个函数中,可以包含任何你想包含的有效的 C++ 语句,使用各种 googletest 断言检查值。
- 测试的结果由断言决定;如果测试中的任何断言失败了(致命的或非致命的),或者如果测试崩溃了,则整个测试失败。否则,测试成功。
使用TEST() 宏来编写一个测试程序,示例代码如下:
#include "gtest/gtest.h"
//==================================
// 被测试函数
//==================================
// 此函数用于判断入参是否为正整数:如果是,则返回0;否则,返回-1
int Positive(int nNum)
{
if (nNum > 0)
{
return 0;
}
else
{
return -1;
}
}
//==================================
// 上述函数的Test Suite看起来可能像下面这样
//==================================
// 测试入参“2”、“4”、“5”是否为正整数
TEST(PositiveTest, HandlesPositiveInput)
{
EXPECT_EQ(Positive(2), 0);
EXPECT_EQ(Positive(4), 0);
EXPECT_EQ(Positive(5), 0);
}
// 测试入参“0”是否为正整数
TEST(PositiveTest, HandlesZeroInput)
{
EXPECT_EQ(Positive(0), -1);
}
// 测试入参“-2"、”-5“是否为正整数
TEST(PositiveTest, HandlesNegativeInput)
{
EXPECT_EQ(Positive(-2), -1);
EXPECT_EQ(Positive(-5), -1);
}
//==================================
// main函数
//==================================
int main(int argc, char **argv)
{
// 分析gtest程序的命令行参数
testing::InitGoogleTest(&argc, argv);
// 调用RUN_ALL_TESTS()运行所有测试用例
// main函数返回RUN_ALL_TESTS()的运行结果
return RUN_ALL_TESTS();
}
在上述代码中,我们编写了三个test,分别为HandlesPositiveInput、HandlesZeroInput 和 HandlesNegativeInput,这三个test 都属于同一个Test Case(PositiveTest)。
编译并执行上述代码,结果类似于下面:
TEST_F()宏
Test Fixture(测试夹具):多个测试使用相同的数据配置。
如果你发现你写了两个或更多测试操作类似的数据,你可以使用一个测试夹具。它允许你为多个不同的测试复用相同的对象配置。
TEST_F()参数
当使用测试夹具时,使用 TEST_F()
而不是 TEST()
,它允许你访问测试夹具中的对象和子例程(你可能已经猜到:_F
指 fixture
):
TEST_F(TestFixtureName, TestName) {
... test body ...
}
像TEST()
一样,第一个参数是测试套件的名字,但对于 TEST_F()
,这必须是Test Fixture类的名字。
TEST_F()测试
创建一个测试夹具:
- 派生一个继承
::testing::Test
的类,并将该类中的一些内容声明为protected
类型,以便在子类中对夹具中的成员进行访问; - 在类内部声明任何你打算使用的对象;
- 根据实际情况,编写默认的构造函数或
SetUp()
函数,来为每个 test 准备所需内容(一个常见的错误是把SetUp()
拼成了Setup()
); - 根据实际情况,编写默认的析构函数或
TearDown()
函数,来释放SetUp()
中分配的资源(要学习何时你应该使用构造函数/析构函数以及何时你应该使用SetUp()/TearDown()
,请阅读 FAQ。); - 根据实际情况,定义 test 共享的子程序。
当使用Test Fixture
时,使用 TEST_F()
而不是 TEST()
,TEST_F()
允许你访问Test Fixture
中的对象和子例程。
你必须在使用 TEST_F()
之前先定义一个Test Fixture
类,否则你将得到一个编译器错误 “virtual outside class declaration”。
对于通过 TEST_F()
定义的每个test,googletest 将在运行时创建一个 全新的test fixture,并立即通过 SetUp() 对其进行初始化,然后运行test,之后通过调用 TearDown() 进行数据清理,最后删除test fixture。注意: 同一个 test case 中不同的 test 具有不同的 test fixture 对象,并且 googletest 每次创建新的 test fixture 前都会先删除之前的 test fixture。多个 test 不会重用相同的 test fixture,某个 test 对 fixture 进行的修改对其他 test 无影响。
使用TEST_F()宏编写一个测试程序,示例代码如下:
#include "gtest/gtest.h"
//==================================
// 定义Test Fixture 类FooTest
//==================================
class FooTest: public ::testing::Test {
protected:
// Code here will be called immediately after the constructor (right before each test)
void SetUp()
{
m_nTarget = 5;
}
// Code here will be called immediately after each test (right before the destructor)
void TearDown()
{
}
public:
int IsLargeThan5(const int & nNum);
int m_nTarget;
};
// 判断入参是否大于5:如果是,则返回0;否则返回-1
int FooTest::IsLargeThan5(const int & nNum)
{
if (nNum > m_nTarget)
{
return 0;
}
else
{
return -1;
}
}
//==================================
// 编写使用TEST_F()和这个test fixture的测试
//==================================
TEST_F(FooTest, HandlesInput6)
{
EXPECT_EQ(IsLargeThan5(6), 0);
}
TEST_F(FooTest, HandlesInput5)
{
EXPECT_EQ(IsLargeThan5(5), 0);
}
TEST_F(FooTest, HandlesInput4)
{
EXPECT_EQ(IsLargeThan5(4), -1);
}
//==================================
// main函数
//==================================
int main(int argc, char **argv)
{
// 分析gtest程序的命令行参数
::testing::InitGoogleTest(&argc, argv);
// 调用RUN_ALL_TESTS()运行所有测试用例
// main函数返回RUN_ALL_TESTS()的运行结果
return RUN_ALL_TESTS();
}
在上述代码中,我们编写了三个 test,分别为:HandlesInput6、HandlesInput5 和 HandlesInput4,这三个 test 都属于同一个 test case(即 FooTest)。注意,这里的 test case(即 FooTest) 一定要是 test fixture 类。
上述代码中的 test 运行时,主要会进行如下操作:
- googletest 构造一个 FooTest 类的对象(我们称之为 f1);
- f1.SetUp() 函数对 f1 进行初始化;
- 使用对象 f1,运行第一个 test(HandlesInput6);
- f1.TearDown() 在 test 完成后,进行清理工作;
- 对象 f1 被析构。
- 上述5个步骤在另一个 FooTest 类的对象(如 f2)中重复,此次会运行 HandlesInput5。
编译并执行上述代码,结果如下:
调用Test
在上面的代码示例中我们能够看到,调用 Test 的操作是通过 RUN_ALL_TESTS()
宏完成的。
TEST()
和 TEST_F()
隐式地把它们的测试注册给 googletest。因此,不像许多其它的 C++ 测试框架,你无需以运行的顺序把你定义的测试重新列出。
定义好test之后,你可以通过RUN_ALL_TESTS()
来运行他们。RUN_ALL_TESTS()
宏在所有 test 都成功时,返回0;否则返回1。需要注意的是,RUN_ALL_TESTS()
会运行你的链接单元中所有关联的 test,这些 test 可以来自不同的 test case,甚至不同的源文件。
当我们调用 RUN_ALL_TESTS()
宏的时候,会进行以下操作:
- 保存所有 googletest flag 的状态;
- 为第一个 test 创建一个 test fixture 对象;
- 通过 SetUp() 对上一步创建的 test fixture 对象进行初始化;
- 使用 test fixture 对象运行 test;
- 通过 TearDown() 清理 fixture;
- 删除 fixture;
- 还原所有 googletest flag 的状态;
- 为下一个 test 重复上述操作,直到所有的 test 执行完成。
如果发生了致命失败则后续的步骤将被跳过。
注意:
- main() 函数必须要返回 RUN_ALL_TESTS() 宏的结果。这种设计的原理是自动化测试服务是基于测试的退出码来决定它是否通过的,而不是它的 stdout/stderr 输出。
- RUN_ALL_TESTS() 只能运行一次,多次调用会与 googletest 的一些功能(如 thread-safe death tests)发生冲突。
编写main函数
编写 main() 函数时,必须要返回 RUN_ALL_TESTS() 宏的值。
::testing::InitGoogleTest()
函数将会解析命令行中的 googletest 参数,并移除所有已识别的标记。它允许用户通过多样的命令行参数来控制测试程序的行为(即定制命令行参数的功能)。
需要注意的是,::testing::InitGoogleTest()
函数必须要在 RUN_ALL_TESTS()
之前调用,否则对应的 flag 可能不会被正常地初始化。
备注
Google Test 是线程安全的,其线程安全特性要依赖 pthreads 库。
参考:
github原文
“Googletset入门”
“Google Test 介绍(一)”
“Google Test 介绍(二)”