软件开发完成后需要对代码进行测试,生成测试报告,因此开始学习单元测试。本文基于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++14
set(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:此函数传的参数2 |
ASSERT_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可以
例如:
class myTesting: public testing::TestWithParam<int>
{
public:
static void SetUpTestCase()//testsuit
{
cout<<"SetUpTestCase"<<endl;
}
static void TearDownTestCase()//testsuit
{
cout<<"TearDownTestCase"<<endl;
}
virtual void SetUp()//testcase
{
cout<<"SetUp"<<endl;
}
virtual void TearDown()
{
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内容如下文章来源:https://www.toymoban.com/news/detail-451028.html
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="3" failures="1" errors="0" time="0.035" timestamp="2011-10-31T18:52:42" name="AllTests">
<testsuite name="MathTest" tests="2" failures="1" errors="0" time="0.015">
<testcase name="Addition" file="test.cpp" line="1" status="run" time="0.007" classname="">
<failure message="Value of: add(1, 1)
 Actual: 3
Expected: 2" type="">...</failure>
<failure message="Value of: add(1, -1)
 Actual: 1
Expected: 0" type="">...</failure>
</testcase>
<testcase name="Subtraction" file="test.cpp" line="2" status="run" time="0.005" classname="">
</testcase>
</testsuite>
<testsuite name="LogicTest" tests="1" failures="0" errors="0" time="0.005">
<testcase name="NonContradiction" file="test.cpp" line="3" status="run" time="0.005" classname="">
</testcase>
</testsuite>
</testsuites>
下一篇学习内容是“怎么生成代码覆盖率报告”和“测试代码和功能代码隔离开”文章来源地址https://www.toymoban.com/news/detail-451028.html
到了这里,关于Linux 单元测试学习过程 (1)——gtest的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!