单元测试是一种保证代码质量的手段。程序员可以通过写单元测试来保证自己写的代码的功能正确。
本人所在公司使用GoogleTest测试框架来进行单元测试。虽然现在在公司的工程代码中写单元测试已经驾轻就熟,但是只是知其然不知其所以然,所以再此对Google Test测试框架进行一个全面的了解与学习。
本文只涉及Google Test的使用,不涉及对其源码的学习与解读。
Linux环境搭建Google Test测试环境
Google Test托管在github上,先通过git把Google Test拉取到本地
git clone https://github.com/google/googletest.git
Google Test 提供了多平台的构建方式,在Linux上推荐使用cmake来编译
cd googletest
cmake .
编译完成之后生成静态库,以后编译自己的单元测试需要链接这些静态库
最后,安装,将头文件和编好的库文件放在系统指定目录下,此步骤需要root权限
make install
安装完成之后,可以尝试编译运行Google Test自带的sample
cd googletest/googletest/make make ./sample1_unittest
运行结果如图所示:
想要从头开始学些如何使用Google Test,去研究一下googletest/googletest/samples下的所有测试用例和googletest/googletest/doc目录下的指导文档,把这些搞清楚了,也就初步掌握了Google Test了。
基本测试
//myGtest.cpp #include <gtest/gtest.h> int add(int a, int b) //待测试函数 { return a + b; } TEST(TestAdd, normal)//单元测试 { EXPECT_EQ(3, add(1, 2)); EXPECT_EQ(4, add(1, 2)); } int main(int argc, char **argv)//初始化GoogleTest并运行所有单元测试 { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
编译:
g++ myGtest.cpp -lgtest -lpthread -o myGtest
注意不仅要链接libgtest.a还要链接libpthread.a
运行结果:
可以看到测试结果,符合add函数的预期行为。
对于基本测试行为,必须包含以下的要素:
1. 测试文件必须包含头文件 gtest/gtest.h;
2. 单元测试函数使用宏 TEST,没有返回类型,有两个参数:第一个测试是测试用例名,第二个参数是本测试名;
3. 测试内容和逻辑需要你自己编写,GoogleTest提供了一系列的宏(类似于安断言)来判断你的代码是否符合预期结果;
4. 需要有一个main函数来初始化并执行所有测试,这个main函数的写法是固定的(由于其是固定的,也可以不写main函数,Google Test提供了一个写好的main 函数只要在链接时链接libgtest_main.a即可),RUN_ALL_TESTS()来执行所有的测试。返回值为0说明全部测试通过,1则说明有失败的测试。
Test Fixtures
有些地方也翻译作测试夹具,我感觉找不到对其合适的翻译。
就是将有相同的流程或使用相同数据的测试用例聚合起来,共享同一套流程或同一份数据,这套流程或数据会在每个测试用例运行之前和之后执行固定的操作。
待测试类:
//MyClass.hpp class MyClass { public: int add(int a, int b) { return a + b; } int plus(int a, int b) { return a * b; } };
测试:
//myGtest.cpp #include <gtest/gtest.h> #include <iostream> #include "MyClass.hpp" class TestMyClass : public testing::Test //test fixture类 { protected: void SetUp() override//每个测试运行前调用 { std::cout<<"test start"<<std::endl; } void TearDown() override//每个测试运行后调用 { std::cout<<"test done"<<std::endl; } MyClass mc; }; TEST_F(TestMyClass, testAdd)//测试用例1 { EXPECT_EQ(3, mc.add(1, 2)); } TEST_F(TestMyClass, testPlus)//测试用例2 { EXPECT_EQ(2, mc.plus(1, 2)); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
运行结果:
可以看到测试结果,符合MyClass类的类方法的预期。并且在测试开始和结束时打印出来我们想要的提示信息
当一个测试执行时会发生以下事件:
1. GTest构造TestMyClass对象(我们称之为t1)。
2. t1.SetUp()函数初始化t1。
3. t1的第一个测试testAdd执行。
4. 在测试执行完后调用t1.TearDown()清理现场。
5. t1被析构。
6. 以上步骤重复一遍,这一轮是执行测试testPlus
使用Test Fixtures可以讲一些流程重复或者数据重用的测试用例聚合起来,更方便编写和维护
对于Test Fixtures而言,具有一下几个要素:
1. 从::testing::Test集成一个类。根据我们希望访问子类的fixture成员的权限,限定为protected或public
2. 在类中,定义任何你想使用的对象
3. 如果需要,实现默认的构造函数或SetUp()函数来为每个测试准备数据
4. 如果需要,实现析构函数和TearDown函数来释放SetUp()函数分配的资源
5. 如果需要,请自定义测试需要共享的函数
6. 使用时,用TEST_F代替TEST(在使用TEST_F前必须先定义好Test Fixtures 类)
7. TEST_F无返回值,有两个参数,第一个是Test Fixtures 类名,第二个是测试名
相关链接:
https://github.com/google/googletest/blob/master/googletest/docs/primer.md
https://www.cnblogs.com/panda_lin/p/gtest_primer.html