文章目录
- 前言
- 1 单元测试
- 2 gtest安装
- 3 gtest原理
- 4 断言
-
- 4.1 明确指定或失败
- 4.2 布尔条件
- 4.3 二元比较
- 4.4 谓词断言
- 4.5 死亡测试
- 5 gtest使用
-
- 5.1 测试函数
- 5.2 测试类
- 5.3 测试夹具
- 5.4 类型参数化
- 5.5 事件
- 总结
前言
GoogleTest(GTest) 是谷歌开源的 C++ 单元测试框架。
1 单元测试
单元测试unit testing是指对软件中的最小可测试单元进行检查和验证,包括函数、类、模块、复杂交互逻辑等。gtest 中单元测试以单例模式实现。每个单元测试包含若干个测试套件test suite,测试套件是指一组功能相同的测试脚本或过程。每个测试套件包含多个测试案例test case,测试同一个功能的不同方向。
根据 gtest 官方文档,一个好的单元测试应该满足:
- 独立、可重复:测试案例可单独执行,错误可重复发生。
- 反映测试代码结构:功能完整,体现完备逻辑。对于关联功能,一个功能是否影响其他功能
- 可移植、可复用:跨平台。
- 尽可能多的出错信息:不会因为一次失败而停止,会继续测试下一个测试案例。一次测试发现多个错误。
- 自动跟踪所有测试而无需枚举:向容器中添加
- 测试高效:测试间重用资源,mock 模拟复杂交互
如何整体使用单元测试?
- 每个类或功能块加上测试案例
- test 目录加上所有的测试
- 一个单元测试可能散落在多个文件
单元测试中的打桩,是指用来代替关联代码或者未实现代码的代码,即用桩函数代替原函数。打桩测试由 gtest 里的 gmock 来实现。
2 gtest安装
# 下载
git clone https://github.com/google/googletest.git
# 安装
cd googletest
cmake CMakeLists.txt
make
sudo make install
源码文件中的 lib 库,包含 gtest 库和 gmock 库。
libgtest.a libgtest_main.a libgmock.a libgmock_main.a
当测试代码有 main 函数,使用不带 main 的静态库,否则使用带 main 的静态库。
# 无 main 函数
g++ sample.cc sample_unittest.cc -o sample -lgtest -lgtest_main -lpthread
# 有 main 函数
g++ gmock_output_test_.cc -o output -lgtest -lgmock -lpthread
若需要编写 main 函数,关键在于添加两个地方
int main(int argc, char **argv) {
// 1.定义 main 函数:初始化 gtest
::testing::InitGoogleTest(&argc, argv);
// 2.定义 main 函数:开启全部测试
return RUN_ALL_TESTS();
}
3 gtest原理
GTest 测试底层原理
- 创建单元测试类,单例模式实现,包含vector<TestSuite*>
- 根据测试套件名,生成一个TestSuite 类实例,包含vector<TestInfo*>;
- 根据测试案例名,生成一个Test_info类实例,继承父类 testing::Test
- 将测试案例实例注册到测试套件中vector<TestSuite*>,测试套件类实例调用run方法执行测试
类的组织层次
// 单元测试,单例模式实现
class Impl {
// 存储测试套件类实例
vector<TestSuite*>;
}
// 测试套件
class TestSuite {
// 存储测试案例类实例
vector<TestInfo*>;
// 执行测试套件中的测试案例
run();
}
// 测试案例
class TestInfo {
// 执行测试
TestBody();
};
4 断言
使用测试断言,通过断言其行为来测试类和函数。ASSERT_* 失败时会生成致命错误,并中止当前功能(但是他会执行测试套件中的下一个测试案例);EXPECT_失败时生成非致命错误,不会中止当前功能。通常选用EXPECT_。
所有断言宏都支持输出流,经流输出的信息自动转换为utf-8,可利用这一特性输出详细错误信息
// 流后面可以使用中文,因为经流输出的信息会自动转换为utf-8
EXPECT_TRUE(my_condition) << "My condition is not true";
4.1 明确指定或失败
当测试案例中的条件太复杂,不能使用断言,那么自己写判断语句;自己返回成功或者失败;比如下面
4.2 布尔条件
EXPECT_TRUE(condition)
ASSERT_TRUE(condition)
EXPECT_FALSE(condition)
ASSERT_FALSE(condition)
4.3 二元比较
// val1 = val2
EXPECT_EQ( val1 , val2 )
ASSERT_EQ( val1 , val2 )
// val1 != val2,空指针使用 nullptr
EXPECT_NE( val1 , val2 )
ASSERT_NE( val1 , val2 )
// val1 <= val2
EXPECT_LT( val1 , val2 )
ASSERT_LT( val1 , val2 )
// val1 > val2
EXPECT_GT( val1 , val2 )
ASSERT_GT( val1 , val2 )
// val1 >= val2
EXPECT_GE( val1 , val2 )
ASSERT_GE( val1 , val2 )
4.4 谓词断言
EXPECT_PREDn( pred , val1, ..., valn ) \
ASSERT_PREDn( pred , val1, ..., valn ) \
例如:测试阶乘函数,参数 1 个
EXPECT_PRED1(Factorial, 1)
说明:
- 谓词断言时,出错时会将参数打印出来,方便查看
4.5 死亡测试
用于测试程序是否以预期的方式崩溃。
EXPECT_DEATH(func, desc);
说明:
- 如果我们写了死亡测试用例,那么调用者读文档时,一看调用相关的参数会崩溃,这将会引起调用者的注意,从而使得它会做一些判断。
5 gtest使用
5.1 测试函数
使用 TEST宏来定义测试案例。
#include "sample1.h"
#include <limits.h>
#include "gtest/gtest.h"
// 使用 TEST 宏定义测试案例
// #define TEST(test_suite_name,test_name)
// 测试阶乘:负数
TEST(FactorialTest, Negative) {
// 断言:预期相等 EXPECT_EQ(expected, actual),后面同理
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
}
// 测试阶乘:0
TEST(FactorialTest, Zero) {
EXPECT_EQ(1, Factorial(0)); }
// 测试阶乘:正数
TEST(</