摘要:本文整体上是一篇读书笔记(末尾有一个自己的实例),主要介绍了TEST()
函数、TEST_F()
函数、Test Fixture、RUN_ALL_TESTS()
,并在运行实例中比较TEST()
和TEST_F()
的差别。
1 Simple Tests
创建一个test函数:
- 通过
TEST()
宏来定义一个test函数。这个是一个没有返回值的普通的C++函数 - 在这个函数中,可以使用任何你想要包括的C++语句并通过GTest提供的各种各样的断言来校验值
- test的结果由断言决定:有任何一个断言失败或程序死机,整个test就是失败的;除此以外,这个test就是成功的
TEST(TestSuiteName, TestName) {
... test body ...
}
TEST()
的参数从一般到具体。第一个参数TestSuiteName是测试套件的名字,第二个参数TestName是这个test在测试套件里的名字。这两个参数都要遵循C++的命名规则并且不应该包含任何下划线。一个test的全名由TestSuiteName+TestName构成。
GTest按照测试套件对test结果进行分组,所以逻辑上相关的test应该放在同一个测试套件里(即TEST()里的第一个参数TestSuiteName应该一致)。
2 Test Fixtures: Using the Same Data Configuration for Multiple Tests
如果你发现自己两个或以上的test作用于相似的数据,你可以使用一个测试固件。测试固件允许你在不同的test中重复使用相同的对象配置
创建一个固件:
- 从 ::testing::Test继承一个类,用
protected:
来开始这个类(类中声明的所有成员都是受保护的),因为我们后续会希望在子类中访问fixture成员 - 在这个类里面,声明任何想要使用的对象
- 如有必要,写一个默认的构造函数或
SetUp()
函数来为每一个test准备对象 - 如有必要,写一个析构函数或
TearDown()
函数来释放在SetUp()或构造函数中分配的资源 - 如有需要,编写子函数使其可被test共享
当使用fixture时,使用TEST_F 代替TEST(),TEST_F()可以让你访问fixture中的对象和子程序
TEST_F(TestFixtureName, TestName) {
... test body ...
}
对于每个用TEST_F()
定义的test,在运行时,GTest会创建一个全新的fixture,通过SetUp()
初始化它,然后运行test,最后用TearDown清理它。请注意在同一个test suite中的不同的test拥有不同的fixture对象,并且GTest会在创建下一个新的fixture对象前删除一个fixture对象。对于多个test,GTest并不会重用fixture对象。因而任何在test中对fixture做出的改变并不会影响到其它的test。
3 Invoking the Tests
TEST()
和TEST_F()
通过GTest隐式的注册了test,因此,使用时并不需要像很多其他的C++测试框架一样,再把test重新罗列一遍来达到执行test的目的。
在定义完test后,通过RUN_ALL_TESTS()
就可以执行定义的test,若所有的test执行成功返回0,反之返回1。 注意,RUN_ALL_TESTS()运行链接单元中的所有test——它们可以来自不同的测试套件,甚至可以来自不同的源文件。
当调用RUN_ALL_TESTS()
时,会进行如下步骤:
- 保存所有GTest标志的状态
- 为第一个test创建一个test fixture
- 通过
SetUp()
初始化test fixture - 在这个fixture对象上执行此test
- 通过
TearDown()
清理这个fixture - 删除这个fixture
- 恢复所有GTest标志的状态
- 下一个test重复上述步骤,直到所有的test执行完成
如果执行过程中发生致命故障,会跳过后续步骤
4 Writing the main() Function
直接把官网给的模板示例复制到cpp文件中,添上自己的test即可
5 TEST()和TEST_F()的区别
使用TEST_F()的源码:
#include "user.h"
#include "stdio.h"
#include "gtest/gtest.h"
namespace my {
namespace project {
namespace {
// The fixture for testing class Foo.
class UserTest : public ::testing::Test {
protected:
// You can remove any or all of the following functions if their bodies would
// be empty.
static void SetUpTestSuite() {
// Avoid reallocating static objects if called in subclasses of FooTest.
printf("UserTest类TestSuite级别SetUp函数执行!\n");
}
// Per-test-suite tear-down.
// Called after the last test in this test suite.
// Can be omitted if not needed.
static void TearDownTestSuite() {
printf("UserTest类TestSuite级别TearDown函数执行!\n");
}
UserTest() {
// You can do set-up work for each test here.
printf("UserTest类构造函数执行!\n");
}
~UserTest() override {
// You can do clean-up work that doesn't throw exceptions here.
printf("UserTest类析构函数执行!\n");
}
// 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).
printf("UserTest类TestCase级别SetUp函数执行!\n");
}
void TearDown() override {
// Code here will be called immediately after each test (right
// before the destructor).
printf("UserTest类TestCase级别TearDown函数执行!\n");
}
// 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(UserTest, TestSetUserName) {
User user;
char name[26];
strcpy_s(name, "wei");
user.setusername(name);
EXPECT_EQ(strcmp(user.getusername(),name), 0);
}
// Tests that Foo does Xyz.
TEST_F(UserTest, TestSetCode) {
// Exercises the Xyz feature of Foo.
User user1;
char code[26];
strcpy_s(code, "wei");
user1.setcode(code);
int result;
result = strcmp(user1.getcode(), code);
EXPECT_EQ(result, 0);
}
TEST_F(UserTest, TestInt) {
// Exercises the Xyz feature of Foo.
int a, b;
a = 1;
b = 2;
EXPECT_EQ(a+1,b);
}
} // namespace
} // namespace project
} // namespace my
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
执行结果:
使用TEST()的源码,只是把上面源码中的TEST_F()改成了TEST(),其它没有任何变化:
#include "user.h"
#include "stdio.h"
#include "gtest/gtest.h"
namespace my {
namespace project {
namespace {
// The fixture for testing class Foo.
class UserTest : public ::testing::Test {
protected:
// You can remove any or all of the following functions if their bodies would
// be empty.
static void SetUpTestSuite() {
// Avoid reallocating static objects if called in subclasses of FooTest.
printf("UserTest类TestSuite级别SetUp函数执行!\n");
}
// Per-test-suite tear-down.
// Called after the last test in this test suite.
// Can be omitted if not needed.
static void TearDownTestSuite() {
printf("UserTest类TestSuite级别TearDown函数执行!\n");
}
UserTest() {
// You can do set-up work for each test here.
printf("UserTest类构造函数执行!\n");
}
~UserTest() override {
// You can do clean-up work that doesn't throw exceptions here.
printf("UserTest类析构函数执行!\n");
}
// 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).
printf("UserTest类TestCase级别SetUp函数执行!\n");
}
void TearDown() override {
// Code here will be called immediately after each test (right
// before the destructor).
printf("UserTest类TestCase级别TearDown函数执行!\n");
}
// 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(UserTest, TestSetUserName) {
User user;
char name[26];
strcpy_s(name, "wei");
user.setusername(name);
EXPECT_EQ(strcmp(user.getusername(),name), 0);
}
// Tests that Foo does Xyz.
TEST(UserTest, TestSetCode) {
// Exercises the Xyz feature of Foo.
User user1;
char code[26];
strcpy_s(code, "wei");
user1.setcode(code);
int result;
result = strcmp(user1.getcode(), code);
EXPECT_EQ(result, 0);
}
TEST(UserTest, TestInt) {
// Exercises the Xyz feature of Foo.
int a, b;
a = 1;
b = 2;
EXPECT_EQ(a+1,b);
}
} // namespace
} // namespace project
} // namespace my
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
执行结果:
个人理解,从运行结果看TEST()
函数不会去调用测试类里的SetUp()
和TearDown()
类型的函数,但TEST_F()
会。
要注意在同一个测试类中,TEST()
和TEST_F()
不能混用,不然会报错。
此例子中还涉及了GTest的事件机制,会在下一篇中介绍。