Linux 单元测试学习过程 (1)——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++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内容如下

<?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)&#x0A;  Actual: 3&#x0A;Expected: 2" type="">...</failure>
      <failure message="Value of: add(1, -1)&#x0A;  Actual: 1&#x0A;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>

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

  • 25
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值