目录
1.Gtest介绍
gtest是一个跨平台的(Liunx、Mac OS X、Windows、Cygwin、Windows CE and Symbian)C++单元测试框架,由google公司发布。gtest是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。
2.Gtest安装
通过github下载gtest
mkdir ~/gtest
cd ~/gtest
git clone https://github.com/google/googletest.git
有可能会有链接git失败的问题,可以采取从github下载压缩包,传到需要的位置解压。
3.Gtest编译
3.1 普通编译
Gtest提供了两种方式编译源码库:automake和cmake,在此我们将以cmake方式进行编译。
编译之前我们先创建一个编译目录gtest-build,这样避免在源码目录进行编译造成污染问题。
(如果需要用到Gtest提供的例子的话,请用下面example编译方法进行编译。)
mkdir ~/gtest/gtest-build-x86
cd gtest-build-x86
cmake ../googletest-main -DCMAKE_INSTALL_PREFIX=./_install
make && make install
3.2 交叉编译
上述普通编译也就是x86的环境下编译,我们采用的J5的工具链编译(尚未解决)。
mkdir ~/gtest/gtest-build-J5
cd gtest-build-J5
cmake ../googletest-main -DCMAKE_INSTALL_PREFIX=./_install -DCMAKE_CXX_COMPILER=${TOOLCHAIN_PATH}/bin/aarch64-rockchip-linux-gnu-g++
make && make install
在”-DCMAKE_CXX_COMPILER=“后加我们使用的交叉编译链的路径即可。
3.3 example编译
Gtest源码库直接提供了大量的测试用例,我们可以直接拿来参考和学习。
首先我们可以直接进行编译并运行测试进行感知Gtest的使用过程。那么应该如何编译呢,我们只需在上面编译的选项中添加一个宏即可:gtest_build_samples=ON,然后重新编译将会在googlemock/gtest看到多 了一些可执行文件:
cd gtest-build-x86
cmake ../googletest -Dgtest_build_samples=ON -DCMAKE_INSTALL_PREFIX=./_install
make && make install gtest_build_samples=ON
在编译的选项当中添加一个宏的方法为在编译时在其后加“-D...”,我在cmake和make时都加了才看到成功。文档中gtest的例子编译后会出现在googlemock中,实际我编译后例子出现在googletest中。
.
├── CMakeFiles
├── cmake_install.cmake
├── CTestTestfile.cmake
├── generated
├── Makefile
├── sample10_unittest
├── sample1_unittest
├── sample2_unittest
├── sample3_unittest
├── sample4_unittest
├── sample5_unittest
├── sample6_unittest
├── sample7_unittest
├── sample8_unittest
└── sample9_unittest
4.使用测试
// gtest_sum.cpp
#include <iostream>
#include <gtest/gtest.h>
int sum(int a, int b) {
return a+b;
}
TEST(sum, testSum) {
EXPECT_EQ(5, sum(2, 3)); // 求合2+3=5
EXPECT_NE(3, sum(3, 4)); // 求合3+4 != 3
}
// 如果在此处不写main函数,那么在链接库的时候还需要链接-lgtest_main, 否则只需链接-lgtest即可。
#if 0
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
#endif
4.1 测试程序解读
首先,测试时使用gtest需包含头文件 gtest/gtest.h, 并链接库 gtest_main.lib 和 gtest.lib.
程序中sum函数是我们要进行测试的函数名或称为分类名。
Test后跟的即为我们要编写的测试方法:
TEST(分类名, 测试名) {
测试代码
也是如何测试,设置测试力
}
使用下面套路化的main函数启动测试:
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}//上述代码中也表明,可不写main函数,但需链接-lgtest_main
可在类中添加测试为类的友元函数(尚未接触):
FRIEND_TEST(分类名, 测试名);
测试代码含义:
TEST(sum, testSum) {
EXPECT_EQ(5, sum(2, 3)); // 求合2+3=5
EXPECT_NE(3, sum(3, 4)); // 求合3+4 != 3
}
以上面代码段举例,EXPECT_字段的表达的是检查方式,除个别检查外,所有检查都带前缀,在下述两项中二选一:
- ASSERT_ : 断言, 不通过检查则中断测试, 当在测试外使用时要求函数返回void.
- EXPECT_ : 期望, 不通过检查并不中断测试.
后缀EQ表达的意思为5 == sum(2,3);
后缀NE表达的意思为3 !=sum(3,4);
以下列举给test中对数值的测试后缀:
后缀 | 参数 | 通过条件 |
---|---|---|
TRUE | © | c == true |
FALSE | © | c == false |
EQ | (a, b) | a == b |
NE | (a, b) | a != b |
LT | (a, b) | a < b |
LE | (a, b) | a <= b |
GT | (a, b) | a > b |
GE | (a, b) | a >= b |
FLOAT_EQ | (a, b) | float型 a ≈ b |
DOUBLE_EQ | (a, b) | double型 a ≈ b |
NEAR | (a, b, e) | abs(a - b) <= e |
HRESULT_SUCCEEDED | (h) | SUCCEEDED(h) == true |
HRESULT_FAILED | (h) | FAILED(h) == true |
对字符串的检查后缀:
后缀 | 参数 | 通过条件 |
---|---|---|
STREQ | (a, b) | C字符串相等 |
STRNE | (a, b) | C字符串不相等 |
STRCASEEQ | (a, b) | C字符串忽略大小写相等 |
STRCASENE | (a, b) | C字符串忽略大小写不相等 |
程序流检查:
更多对于检查测试的后缀:
[C++] gtest入门教程_西北丰的博客-CSDN博客_c++ gtest
4.2 测试程序编译
编译测试程序:
g++ -o gtest_sum gtest_sum.cpp -I${HOME}/gtest/gtest-build/_install/include \
-L${HOME}/gtest/gtest-build/_install/lib -lgtest_main -lgtest -lpthread -std=c++11
注:编译时将换行符去掉,注意程序中的注释选择是否链接库。
编译完成后将会产出一个gtest_sum的可执行文件。
执行:
./gtest_sum
Running main() from /home/sven/work/3rd/googletest/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from sum
[ RUN ] sum.testSum
[ OK ] sum.testSum (0 ms)
[----------] 1 test from sum (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
运行成功后如上所示。
添加如下测试力:
EXPECT_NE(7, sum(3, 4));
编译后,运行显示:
Running main() from /build/googletest-j5yxiC/googletest-1.10.0/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from sum
[ RUN ] sum.testSum
gtest_sum.cpp:12: Failure
Expected: (7) != (sum(3, 4)), actual: 7 vs 7
[ FAILED ] sum.testSum (0 ms)
[----------] 1 test from sum (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] sum.testSum
1 FAILED TEST
由此得出,检查到错误后,会反馈那个检查错误,错误行数,错误原因。
5. 测试实例
所学习到的测试方法分为四种:基本测试、测试固件、异常测试、值参数化测试。
其代码及分析:
https://blog.csdn.net/kx_nullpointer/article/details/7238305
可将被测试函数放置头文件中,Test测试力函数放在.cpp文件中,也可同时放在.cpp文件中。修改4.2中编译命令即可生成可执行文件进行测试。
5.1 基本测试
在最基础的测试中,一般只需要包含头文件gtest.h即可。gtest中常用的所有结构体、类、函数、常量等,都通过命名空间testing访问,但是gtest已经把常用的单元测试功能包装成了带参数宏,所以简单测试中可以忽略命名空间。
Test宏开头便定义了一个可执行测试,其参数为被测函数和自定义测试名称,自定义名称也可为中文,例如TEST(Add, 正数){},{}中添加逻辑代码,形成测试力,测试力要求自拟并利用宏达到自己想要的结果。
每个宏也可以使用 << 运算符在测试失败时输出自定义信息,如:
ASSERT_EQ(M[i], N[j]) << "i = " << i << ", j = " << j;
5.2 测试固件
测试固件是可以为所有测试创建一个相同的配置环境,并在测试结束后执行清理工作。对测试固件的使用比基本测试多出来的方法是需要继承Test的类:
- 从gtest的testing::Test类继承,用public或protect定义以下成员;
- (可选)整个测试环境的开始和结束,使用默认构造函数和析构函数函数分别为SetUpTestCase()和TearDownTestCase();
- (可选)建立环境:使用默认构造函数,或定义一个虚成员函数virtual void SetUp();
- (可选)销毁环境:使用析构函数,或定义一个虚成员函数virtual void TearDown();
- 用TEST_F定义测试,写法与TEST相同,但测试用例名即被测试对象必须为上面定义的类名。
代码和测试结果如下:
class TestSuit1 : public ::testing::Test {
public:
static void TearDownTestCase()
{
std::cout << "TearDownTestCase" << std::endl;
}
static void SetUpTestCase()
{
std::cout << "SetUpTestCase" << std::endl;
}
virtual void SetUp()
{
std::cout << "SetUp" << std::endl;
}
virtual void TearDown()
{
std::cout << "TearDown" << std::endl;
}
};
int testFunc(int a, int b)
{
return a + b;
}
// 注意TEST_F
TEST_F(TestSuit1, testCase1)
{
std::cout << testFunc(1, 3) << std::endl;;
}
TEST_F(TestSuit1, testCase2)
{
std::cout << testFunc(2, 3) << std::endl;;
}
Running main() from /build/googletest-j5yxiC/googletest-1.10.0/googletest/src/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from TestSuit1
SetUpTestCase
[ RUN ] TestSuit1.testCase1
SetUp
4
TearDown
[ OK ] TestSuit1.testCase1 (0 ms)
[ RUN ] TestSuit1.testCase2
SetUp
5
TearDown
[ OK ] TestSuit1.testCase2 (0 ms)
TearDownTestCase
[----------] 2 tests from TestSuit1 (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 2 tests.
5.3 异常测试
gtest为异常测试提供了专用的宏:
ASSERT宏 | EXPECT宏 | 功能 |
---|---|---|
ASSERT_NO_THROW | EXPECT_NO_THROW | 不抛出异常,参数为 (statement) |
ASSERT_ANY_THROW | EXPECT_ANY_THROW | 抛出异常,参数为 (statement) |
ASSERT_THROW | EXPECT_THROW | 抛出特定类型的异常,参数为 (statement, type) |
此异常测试是测试被测对象是否在运行中出现我们测试力规定的异常,如果有异常会执行通过,宏的功能是抛出 异常,但实际无法获得被抛出异常的详细信息(如报错文本)。如使用_THROW宏,遇到异常会判断出现的异常与我们设置的异常是否相同(type),如果不相同会报错,测试失败。
5.4 值参数化测试
值参数化测试即为为被测对象带入大量随机值进行测试,类似于在for循环内使用函数。gtest中的定位是用来使用大量随机值来检验算法实现的正确性,或比较同一个接口的不同实现之间的差别。gtest把“集中输入测试参数”的需求抽象出来提供支持,称为值参数化测试(Value Parameterized Test)。
值参数化测试包括4个步骤:
- 从gtest的TestWithParam模板类继承出一个类,因为TestWithParam本身是从Test派生的,所以我们定义的类也就成了一个测试固件类。
- 在此类中,可实现SetUp、TearDown等方法。测试参数由TestWithParam实现的GetParam()方法依次返回。
- 使用TEST_P(而不是TEST_F)定义测试。
- 使用INSTANTIATE_TEST_CASE_P宏集中输入测试参数,它接受3个参数:任意的文本前缀,测试类名,以及测试参数值序列。gtest框架依次使用这些参数值生成测试固件类实例,并执行用户定义的测试。
gtest提供了专门的模板函数来生成参数值序列,如下表所示:
参数值序列生成函数 | 含义 |
---|---|
Bool() | 生成序列 {false, true} |
Range(begin, end[, step]) | 生成序列 {begin, begin+step, begin+2*step, ...} (不含 end), step默认为1 |
Values(v1, v2, ..., vN) | 生成序列 {v1, v2, ..., vN} |
ValuesIn(container), ValuesIn(iter1, iter2) | 枚举STL container,或枚举迭代器范围 [iter1, iter2) |
Combine(g1, g2, ..., gN) | 生成 g1, g2, ..., gN的笛卡尔积,其中g1, g2, ..., gN均为参数值序列生成函数(要求C++0x的<tr1/tuple>) |
示例代码如下:
// addupto.h
#pragma once
inline unsigned NaiveAddUpTo(unsigned n) {
unsigned sum = 0;
for(unsigned i = 1; i <= n; ++i) sum += i;
return sum;
}
inline unsigned FastAddUpTo(unsigned n) {
return n*(n+1)/2;
}
// addupto_test.cpp
//参数化测试
#include <gtest/gtest.h>
#include "addupto.h"
class AddUpToTest : public testing::TestWithParam<unsigned>//继承此类进行值参数化测试
{
public:
AddUpToTest() { n_ = GetParam(); } //测试参数由此返回
protected:
unsigned n_;
};
TEST_P(AddUpToTest, Calibration) {
EXPECT_EQ(NaiveAddUpTo(n_), FastAddUpTo(n_)); //期望a和b相等,不中断
}
INSTANTIATE_TEST_CASE_P(
NaiveAndFast, // prefix 前缀
AddUpToTest, // test case name 测试用例名称
testing::Range(1u, 1000u) // parameters 参数,u表示unsigned,必需严谨,否则编译错误,
//也可以使用 testing::Range<unsigned>(1, 1000)
);
上述代码编译运行的部分显示:
[ RUN ] NaiveAndFast/AddUpToTest.Calibration/993
[ OK ] NaiveAndFast/AddUpToTest.Calibration/993 (0 ms)
[ RUN ] NaiveAndFast/AddUpToTest.Calibration/994
[ OK ] NaiveAndFast/AddUpToTest.Calibration/994 (0 ms)
[ RUN ] NaiveAndFast/AddUpToTest.Calibration/995
[ OK ] NaiveAndFast/AddUpToTest.Calibration/995 (0 ms)
[ RUN ] NaiveAndFast/AddUpToTest.Calibration/996
[ OK ] NaiveAndFast/AddUpToTest.Calibration/996 (0 ms)
[ RUN ] NaiveAndFast/AddUpToTest.Calibration/997
[ OK ] NaiveAndFast/AddUpToTest.Calibration/997 (0 ms)
[ RUN ] NaiveAndFast/AddUpToTest.Calibration/998
[ OK ] NaiveAndFast/AddUpToTest.Calibration/998 (0 ms)
[----------] 999 tests from NaiveAndFast/AddUpToTest (11 ms total)
[----------] Global test environment tear-down
[==========] 999 tests from 1 test suite ran. (11 ms total)
[ PASSED ] 999 tests.
*注:自己学习分享,如有不对,请指正。