博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++单元测试框架:gtest
阅读量:6606 次
发布时间:2019-06-24

本文共 7766 字,大约阅读时间需要 25 分钟。

  hot3.png

C++单元测试框架:

1) 介绍

是Google的C++测试框架,可以帮助开发者更简单快捷得写好C++单元测试。并且无论你是工作在Linux,Windows还是Mac上。

如果你曾写过单元测试,无论用的什么语言,相信你很快就能上手。如果还不了解,那么就从这里开始吧。

如果你想更深入了解测试框架,可以看Kent Beck大神的。

2) 准备

2.1) 目录树

gtest_start/├─build/            # 构建文件├─lib/              # 第三方库├─output/           # 输出目录(中间文件,执行文件)│  ├─gtest/│  └─primer/│      ├─Debug│      │  └─obj/│      └─Release├─src/              # 工程代码│  └─primer/├─third_party/      # 第三方库代码│  └─gtest/└─tools/            # 工具脚本

这样清理方便些。

2.2) gtest

# 准备目录mkdir -p gtest_start/third_party/cd gtest_start/third_party/# 获取源码svn co http://googletest.googlecode.com/svn/trunk/ gtest

gtest本身提供了多平台的构建文件,如下:

We provide build files for some popular build systems: msvc/ for Visual Studio, xcode/ for Mac Xcode, make/ for GNU make, codegear/ for Borland C++ Builder, and the autotools script (deprecated) and CMakeLists.txt for CMake (recommended) in the Google Test root directory.

如果你用的不是以上这些,则可以看make/Makefile或msvc/gtest.sln工程,参考配置。

主要是将gtest-all.cc生成静态库。如果带gtest_main.cc,就省去了写main函数。

3) 简单测试

3.1) TEST()宏

Step 1: 创建一个简单函数:

bool IsEven(int num) {    return num % 2 == 0;}

Step 2: 用TEST()宏定义并命名一个测试方法:

TEST(test_case_name, test_name) {    ... test body ...}

对于IsEven(),可以这样:

TEST(IsEvenTest, TestA) {    ASSERT_TRUE(IsEven(2));    ASSERT_TRUE(IsEven(3));}

Step 3: 工程引入gtest_main静态库,运行即可。Linux下记得添加-lpthread

运行结果

Running main() from gtest_main.cc[==========] Running 1 test from 1 test case.[----------] Global test environment set-up.[----------] 1 test from IsEvenTest[ RUN      ] IsEvenTest.TestA[       OK ] IsEvenTest.TestA (0 ms)[----------] 1 test from IsEvenTest (1 ms total)[----------] Global test environment tear-down[==========] 1 test from 1 test case ran. (2 ms total)[  PASSED  ] 1 test.

3.2) ASSERT_*()宏

ASSERT_*()宏,当失败时,会生成致命错误,退出当前TEST()。

ASSERT_*()宏相应都会有个EXPECT_*()版本,当失败时,其生成的是非致命错误,会继续当前TEST()。

例如

TEST(IsEvenTest, TestA) {    cout << "IsEvenTest start" << endl;    ASSERT_TRUE(IsEven(3));  // failed    ASSERT_FALSE(IsEven(3));    cout << "IsEvenTest end" << endl;}TEST(PowerTest, TestA) {    cout << "PowerTest start" << endl;    EXPECT_EQ(9, Power(2, 3));  // failed, expected: 8    EXPECT_EQ(27, Power(3, 3));    cout << "PowerTest end" << endl;}

运行结果

# ...[ RUN      ] IsEvenTest.TestAIsEvenTest start..\src\primer\simple_unittest.cc(10): error: Value of: IsEven(3)  Actual: falseExpected: true[  FAILED  ] IsEvenTest.TestA (3 ms)# ...[ RUN      ] PowerTest.TestAPowerTest start..\src\primer\simple_unittest.cc(17): error: Value of: Power(2, 3)  Actual: 8Expected: 9PowerTest end[  FAILED  ] PowerTest.TestA (4 ms)# ...[  FAILED  ] 2 tests, listed below:# ...

注意:没有输出"IsEvenTest end",但有输出"PowerTest end"。

不过只要是失败,都会被统计进最后的tests失败列表。

ASSERT_*()基本的一些宏,请见参考1的。

4) 测试套件

当你写的很多测试都用类似的数据作为输入时,这时应当考虑用test fixture

例如

template
class Calculator {public: T Plus(T lhs, T rhs) { return lhs + rhs; } T Minus(T lhs, T rhs) { return lhs - rhs; } T Multiplies(T lhs, T rhs) { return lhs * rhs; } T Divides(T lhs, T rhs) { return lhs / rhs; }};

测试Plus、Minus、Multiplies、Divides时,可以都用两个同样的Data作为数据。这时,你可以创建一个测试套件:

Step 1: 继承::testing::Test。且以protected:public:开始,以使子类可以访问。

class CalculatorTest : public ::testing::Test {protected:    // ...};

Step 2: 声明你要用的数据。这儿是Data:

Data* data_a_;Data* data_b_;// 以下为对ab进行四种操作后的结果:Data plus_ab;Data minus_ab;Data multiplies_ab;Data divides_ab;

Step 3: 在默认构造函数或SetUp()函数内准备好数据:

CalculatorTest()    : plus_ab(10),      minus_ab(-6),      multiplies_ab(16),      divides_ab(0) {}// Sets up the test fixture.virtual void SetUp() {    data_a_ = new Data(2);    data_b_ = new Data(8);}

Step 4: 在析构函数或TearDown()函数内释放数据:

virtual ~CalculatorTest() {}// Tears down the test fixture.virtual void TearDown() {    delete data_a_;    data_a_ = nullptr;    delete data_b_;    data_b_ = nullptr;}

Step 5: 用TEST_F()宏来做测试,它允许你访问套件内成员:

TEST_F(CalculatorTest, Plus) {    Calculator<> calculator;    EXPECT_EQ(plus_ab, calculator.Plus(*data_a_, *data_b_));}

运行结果

Running main() from gtest_main.cc[==========] Running 1 test from 1 test case.[----------] Global test environment set-up.[----------] 1 test from CalculatorTest[ RUN      ] CalculatorTest.Plus[       OK ] CalculatorTest.Plus (0 ms)[----------] 1 test from CalculatorTest (1 ms total)[----------] Global test environment tear-down[==========] 1 test from 1 test case ran. (4 ms total)[  PASSED  ] 1 test.

但这里,如果同时测试四个,Calculator<> calculator;岂不是要创建四次。因此我们可以考虑将它共享:

Step 1: 定义为一个static成员:

class CalculatorTest : public ::testing::Test {protected:    // ...    // Shared by all tests.    static Calculator<>* shared_calculator_;};

当然,别忘记初始化:

Calculator<>* CalculatorTest::shared_calculator_ = nullptr;

Step 2: 在static SetUpTestCase()函数内准备数据:

// Sets up the stuff shared by all tests in this test case.static void SetUpTestCase() {    shared_calculator_ = new Calculator<>;}

Step 3: 在static TearDownTestCase()函数内释放数据:

// Tears down the stuff shared by all tests in this test case.static void TearDownTestCase() {    delete shared_calculator_;    shared_calculator_ = nullptr;}

Step 4: 然后,TEST_F()宏做测试:

TEST_F(CalculatorTest, Plus) {    EXPECT_EQ(plus_ab, shared_calculator_->Plus(*data_a_, *data_b_));}

上述是同一个测试用例下共享数据。另外还有全局环境的,见。

5) 总结

本文仅仅是初步体验了gtest,另外还有死亡测试,事件监听,以及重复测试、临时禁用等选项。具体请见参考2。

除此之外,其他一些有用的文章也列在了参考里。

6) 参考

以上两个官方文档,非常详细。有必要阅览一遍,并可作为手册。

另外,这个系列也很不错:。把官方文档主要的一些内容都讲述了遍。

还有上的几篇:

    • 介绍了下测试驱动开发,并比较了Gtest,Boost Test,CPPUnit,以及三子棋游戏的实践。
    • googletest与googlemock的介绍和使用。

附1:样例工程gtest_start

下载:。

1) 构建文件

build/gtest_start-gcc.cbp   # for gnu gccbuild/gtest_start-msvc.cbp  # for msvc 2010

.cbp由C::B打开即可。

2) 补充说明

"src/primer/"下为本文例子,而"src/thoughts/"下是我以前写的内容:

├─src/│  ├─primer/                # 本文例子│  └─thoughts/│      ├─designpattern/     # C++编程思想设计模式一节例子及其测试│      ├─gtest/             # gtest原生sample,queue测试例子│      └─stl/               # stl的测试例子

"designpattern/"下例子,"-Wall"有很多警告,也确实是有些问题的,不用太在意。

参考

  1. C::B安装:

附2:升级GCC后,动态库链接问题

1) 问题现象

Linux下运行程序时,报出"/usr/lib/libstdc++.so.6: version 'GLIBCXX_3.4.14' not found"的问题。

2) 问题原因

先前我升级GCC到了4.8.2,且只是软链接到了高版本的gcc,g++。不知道还要对应升级下libstdc++库。见:。

3) 解决流程

Step 1: 命令检查libstdc++.so使用的GLIBC版本:

strings /usr/lib/libstdc++.so.6 | grep GLIB

输出:

GLIBCXX_3.4GLIBCXX_3.4.1...GLIBCXX_3.4.12GLIBCXX_3.4.13GLIBC_2.0...GLIBC_2.2GLIBCXX_FORCE_NEWGLIBCXX_DEBUG_MESSAGE_LENGTH

没发现'GLIBCXX_3.4.14'。

Step 2: 检查/usr/lib目录下的libstdc++的库文件:

ll /usr/lib/libstdc++*

输出:

lrwxrwxrwx. 1 root root     19 Mar 18 04:57 /usr/lib/libstdc++.so.6 -> libstdc++.so.6.0.13-rwxr-xr-x. 1 root root 942040 Nov 21 07:28 /usr/lib/libstdc++.so.6.0.13

检查下安装:

yum list libstdc++*yum install libstdc++

已经是最高版本了。

Step 3: 由GCC版本,搜索了下'libstdc++-4.8.2'。

gcc --version# gcc (GCC) 4.8.2

发现这篇:,看命令源码目录下有'libstdc++-v3',确实。

cd /home/join/Env/gcc/gcc-4.8.2/find ./ -name libstdc++.so*

输出:

./stage1-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6./stage1-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so./stage1-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.18./i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6./i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so./i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.18./prev-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6./prev-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so./prev-i686-pc-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.18

我这边的环境,有三个路径下有libstdc++.so.6.0.18,libstdc++.so与libstdc++.so.6只是其软链接。

查看各路径下libstdc++.so.6.0.18状态:

stat libstdc++.so.6.0.18

发现各路径下libstdc++.so.6.0.18都一样大小。stage1前缀路径下最早生成,之后prev前缀的,不带前缀的最晚。除了生成时间,另有个Inode不同,存储在文件系统的索引节点(必然的)。

不清楚Inode,所以了解了下:,。

Step 4: 替换老版本libstdc++库:

# 复制到'/usr/lib/'cd ./i686-pc-linux-gnu/libstdc++-v3/src/.libs/cp libstdc++.so.6.0.18cp libstdc++.so.6.0.18 /usr/lib/# 重新建立软链接rm -f libstdc++.so.6ln -s libstdc++.so.6.0.18 libstdc++.so.6

参考

转载于:https://my.oschina.net/vaero/blog/214893

你可能感兴趣的文章
关于Nature的.net版框架
查看>>
Hp DL380服务器硬盘故障数据恢复过程
查看>>
RAID磁盘阵列技术及数据恢复原理
查看>>
JAVA 动态配置 (配置源={properties,redis})
查看>>
python计算IV值及使用
查看>>
PyCharm的快捷键大全
查看>>
创建型模式:抽象工厂
查看>>
解决键盘弹出时,webview被挤压导致背景图片被挤压出空白
查看>>
30天提升技术人的写作力-第二十四天
查看>>
python_socket编程
查看>>
可重入函数和线程安全
查看>>
项目管理师高项,跟薛老师一个月搞定
查看>>
如何看证券期货业IT备份标准草案
查看>>
内核程序[驱动开发],第一步
查看>>
在Myeclipse中创建自定义用户类库
查看>>
如何系统地学习数据挖掘
查看>>
Mysql 数据库密码管理
查看>>
bean 作用 域
查看>>
我的友情链接
查看>>
Python 编程中常用的12种基础知识总结
查看>>