0


Linux 单元测试学习过程 (1)——gtest

gtest学习

软件开发完成后需要对代码进行测试,生成测试报告,因此开始学习单元测试。本文基于QT和Linux进行学习。学习目的就是生成测试报告。整个学习过程主要围绕“1.怎么进行单元测试”、“2.怎么生成测试过程的结果文件”、“3.怎么生成代码覆盖率报告”。
1.使用gtest进行单元测试
2.gtest有生成测试报告,但是可读性差
3.结合gcov和lcov生成可视化代码覆盖率报告
敏捷开发应该是测试驱动开发,应该先有测试才有功能函数。

gtest

使用gtest应该先看一下gtest官方文档,官方文档对如何使用gtest描述得挺清楚。
gtest资源下载链接。

gtest下载编译

//gtest依赖gcc 5.0+,查看本地gcc版本
gcc -v
//下载gtest资源后解压
unzip googletest-main
//解压后进入解压文件夹
mkdir build
cd build
cmake ..
make

编译成功后,在解压文件夹/bulid/lib/中生成了需要用到的库(文件后缀*.a,后面需要添加到工程中),在解压文件夹/googlemock/include/,解压文件夹/googletest/include/内的文件后面也需要添加到工程中。

gtest导入工程

gtest导入工程处理首先将上面提到的三个目录复制到工程目录下,然后需要将gtest所在的目录信息添加到CMakeLists.txt中。

# 第一,要添加这个GoogleTest requires at least C++14set(CMAKE_CXX_STANDARD 14)
#第二,增加googletest/include/的绝对路径
include_directories(
    添加googletest/include/的目录绝对路径
)
#第三, 增加googletest/lib/的目录绝对路径
link_directories(
    添加googletest/lib/的目录绝对路径
)
#第四,增加库的名字
target_link_libraries(工程名
    gtest gtest_main
    -lpthread -lm ##pthread库(Google Test使用了这个库所以需要)
)

添加保存完后,在终端

#在build目录下
ccmake .. 或 cmake ..#成功后就编译
make

编写测试单元用的宏(类似于函数)

断言

基本断言可以分为两类,一类是ASSERT系列,一类是EXPECT系列。

  • ASSERT_系列的断言(Fatal assertion):当检查点失败时,退出当前函数(注意:并非退出当前案例)。一个TEST代表一个案例或测试用例
  • EXPECT_系列的断言(Nonfatal assertion):当检查点失败时,继续执行下一个检查点(每一个断言表示一个检查点)。

通常情况应该首选使用EXPECT_*,因为ASSERT_*在报告完错误后不会进行清理工作,有可能导致内存泄露问题。
其中"<<"输出错误时自定义的log信息。例:

EXPECT_EQ(/*希望被测试函数返回的结果*/,/*被测试函数实际返回的结果*/)<<"测试不通过时才输出";//

更多常用的断言描述可参考gtest单元测试框架介绍及简单使用
不常用但重要的断言断言名参数意义*_PRED*EXPECT_PRED1(pred,val1)pred:返回值为bool的函数,此函数参数只有一个,写函数名就好。 val1:此函数传的参数*_PRED*断言与*_TRUE断言相似,但在失败时*_PRED*断言能打印更详细的信息EXPECT_PRED2(pred,val1,val2)pred:返回值为bool的函数,此函数参数为两个,写函数名就好。 val1:此函数传的参数1,val2:此函数传的参数2ASSERT_PRED1(pred,val1)规则同上ASSERT_PRED2(pred,val1,val2)规则同上后续将继续补充其他断言…

测试宏

测试宏分三类:TEST宏、TEST_F宏、TEST_P宏
TEST宏:
TEST(自定义命名,自定义命名),第一个参数testsuit名,相同的命名代表 同一个testsuit;第二个参数testcase名,测试案例名。这两个参数根据实际设计的测试用例进行命名。
TEST_F宏:
TEST_F(测试类的名字,自定义命名),第一个参数代表testsuit名,相同的命名代表同一个testsuit;第二个参数testcase名,测试案例名。结合测试类使用。
TEST_P宏:
TEST_P(测试类的名字,自定义命名),第一个参数代表testsuit名,相同的命名代表同一个testsuit;第二个参数testcase名,测试案例名。结合测试类和参数生成。
注:TEST与TEST_F的区别是,当需要测试案例之间共享参数时,需要定义一个继承testing::Test的类,TEST不能调用类,但TEST_F可以
例如:

classmyTesting:public testing::TestWithParam<int>{public:staticvoidSetUpTestCase()//testsuit{
        cout<<"SetUpTestCase"<<endl;}staticvoidTearDownTestCase()//testsuit{
        cout<<"TearDownTestCase"<<endl;}virtualvoidSetUp()//testcase{
        cout<<"SetUp"<<endl;}virtualvoidTearDown(){
        cout<<"TearDown"<<endl;}}//生成参数INSTANTIATE_TEST_SUITE_P(Param,myTesting,testing::Values(1,3,5,7));/*测试参数:
* Range(begin,end,step)
* ValuesIn(begin,end)
* Bool()
* combine(,,,)排列组合
*/TEST_P(myTesting,test){int n =GetParam();EXPECT_EQ(n,3)<<"inequal num "<<n;}

事件机制

TestCase事件

  • 在每个案例执行前后
  • 需要多次对类初始时使用
  • string有多个方法
  • 适用情况:一个类,有多个行为。执行先后顺序相关的
  • 需要继承testing::Test
  • 代表一个测试用例
  • TestCase事件是挂在每个案例执行前后的,实现方式和Test’Suites的几乎一样,不过需要实现的是SetUp方法和TearDown方法: 1. SetUp()方法在每个TestCase之前执行。 2. TearDown()方法在每个TestCase之后执行。

TestSuit事件

  • 在某一批案例中,第一个执行前到最后一个执行后
  • 一般用于类行为测试或者其他有联系的多个方法测试
  • 适用情况:多个类,多个函数,有多种组合,用一个变量保存整个过程
  • 继承testing::Test
  • 在某一批测试用例中生命周期中唯一需要写一个类,继承testing::Test,然后实现两个静态方法1. SetUpTestCase() 方法在第一个TestCase之前执行。2. TearDownTestCase() 方法在最后一个TestCase之后执行。

全局事件

所有案例执行前后
可用于组合类行为测试
需要有main函数:testing::AddGlobalTestEnvironment(new 全局测试类名)
             testing::InitGoogleTest(&argc, argv);
继承testing::Environment
整个所有测试用例中有效

测试结果文件

  • 终端输出测试结果
...[----------] 1 test from FooTest
[ RUN      ] FooTest.DoesAbc
[       OK ] FooTest.DoesAbc
[----------] 2 tests from BarTest
[ RUN      ] BarTest.HasXyzProperty
[       OK ] BarTest.HasXyzProperty
[ RUN      ] BarTest.ReturnsTrueOnSuccess
... some error messages ...[   FAILED ] BarTest.ReturnsTrueOnSuccess
...[==========] 30 tests from 14 test suites ran.[   PASSED ] 28 tests.[   FAILED ] 2 tests, listed below:
[   FAILED ] BarTest.ReturnsTrueOnSuccess
[   FAILED ] AnotherTest.DoesXyz

 2 FAILED TESTS
  • 生成xml报告
//在main中增加下面代码,会生成 项目名.xml
testing::GTEST_FLAG(output)="xml:";

xml内容如下

<?xml version="1.0" encoding="UTF-8"?><testsuitestests="3"failures="1"errors="0"time="0.035"timestamp="2011-10-31T18:52:42"name="AllTests"><testsuitename="MathTest"tests="2"failures="1"errors="0"time="0.015"><testcasename="Addition"file="test.cpp"line="1"status="run"time="0.007"classname=""><failuremessage="Value of: add(1, 1)&#x0A;  Actual: 3&#x0A;Expected: 2"type="">...</failure><failuremessage="Value of: add(1, -1)&#x0A;  Actual: 1&#x0A;Expected: 0"type="">...</failure></testcase><testcasename="Subtraction"file="test.cpp"line="2"status="run"time="0.005"classname=""></testcase></testsuite><testsuitename="LogicTest"tests="1"failures="0"errors="0"time="0.005"><testcasename="NonContradiction"file="test.cpp"line="3"status="run"time="0.005"classname=""></testcase></testsuite></testsuites>

下一篇学习内容是“怎么生成代码覆盖率报告”和“测试代码和功能代码隔离开”

标签: c++ 单元测试

本文转载自: https://blog.csdn.net/weixin_45892725/article/details/127820526
版权归原作者 k k ~ 所有, 如有侵权,请联系我们删除。

“Linux 单元测试学习过程 (1)——gtest”的评论:

还没有评论