天天看点

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

gtest学习

  • gtest
    • gtest下载编译
    • gtest导入工程
    • 编写测试单元用的宏(类似于函数)
      • 断言
      • 测试宏
      • 事件机制
        • TestCase事件
        • TestSuit事件
        • 全局事件
    • 测试结果文件

软件开发完成后需要对代码进行测试,生成测试报告,因此开始学习单元测试。本文基于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信息。例:

更多常用的断言描述可参考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>
           

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

继续阅读