C代码的单元测试

 

C代码中集成gtest单元测试_gtest测试c语言_山河故人~的博客-CSDN博客

Linux安装gtest_gtest安装_山河故人~的博客-CSDN博客

一:安装gtest:

1. 安装gtest
采用源码安装的方式,需确保cmake已经安装。

git clone https://github.com/google/googletest
cd googletest
mkdir build
cd build
cmake ..
make
sudo make install

2. 测试安装成功

新建gtest.c文件,输入如下测试代码:

#include<gtest/gtest.h>
int add(int a,int b){return a+b;
}
TEST(testCase,test0){EXPECT_EQ(add(2,3),5);
}
int main(int argc,char **argv){testing::InitGoogleTest(&argc,argv);return RUN_ALL_TESTS();
}

编译可执行文件

g++ gtest.c -lgtest -lpthread -v -o gtest

运行

./gtest


运行结果

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from testCase
[ RUN      ] testCase.test0
[       OK ] testCase.test0 (0 ms)
[----------] 1 test from testCase (0 ms total)[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

以上运行结果代表gtest安装成功

二:C代码中快速集成gtest进行单元测试

1. 原理

将 gtest 框架应用到 C 程序的原理比较简单,主要分 3 步:

1.编译测试框架 googletest 源码得到 libgtest.a 库文件(也可以根据需要编译成动态链接库或共享库文件.so)
2.编译待测试的 C 代码得到一个功能库文件,例如 libfoo.a
3.写一个单元测试文件(如:foo_unitttest.cc), 编译链接上 libgtest.a 和 libfoo.a 生成可执行文件进行测试。

如果代码规模不大,对代码模块化要求也不高的话,可以将第2和第3步合并到一起执行,变成:

1.编译测试框架 googletest 源码得到 libgtest.a 库文件
2.写一个单元测试文件(如:foo_unitttest.cc),和待测试代码一起编译并链接到 libgtest.a 库得到可执行文件进行测试


2. 两步操作快速集成 gtest


2.1 准备 gtest 库文件和头文件

复制下面的代码分别保存为 CMakeLists.txt 和 gtest.mk 文件。

1:CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(gtestlib)# GoogleTest requires at least C++11
set(CMAKE_CXX_STANDARD 11)include(FetchContent)
FetchContent_Declare(googletestURL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip
)# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

2:gtest.mk

.PHONY: all cleanCMAKE   = cmake
MAKE    = makeGTEST             = .GTEST_BUILD_DIR   = $(GTEST)/build
GTEST_INSTALL_DIR = $(GTEST)/gtest
GTEST_CMAKELIST   = $(GTEST)/CMakeLists.txtGTEST_LIB         = $(GTEST_INSTALL_DIR)/lib/libgtest.a
GTEST_LIBMAIN     = $(GTEST_INSTALL_DIR)/lib/libgtest_main.aall:if [ ! -e $(GTEST_LIB) ] || [ ! -e $(GTEST_LIBMAIN) ]; then \mkdir -p $(GTEST_BUILD_DIR) && \$(CMAKE) -S $(GTEST) -B $(GTEST_BUILD_DIR) -DBUILD_GMOCK=OFF -DCMAKE_INSTALL_PREFIX=$(GTEST_INSTALL_DIR) && \$(MAKE) -C $(GTEST_BUILD_DIR) && \$(MAKE) -C $(GTEST_BUILD_DIR) install; \ficlean:if [ -e $(GTEST_BUILD_DIR) ]; then \$(MAKE) -C $(GTEST_BUILD_DIR) clean; \firm -rf $(GTEST_BUILD_DIR)rm -rf $(GTEST_INSTALL_DIR)

将上面的两个文件存放到同一个文件夹下,然后执行make -f gtest.mk。

代码会自动下载 googletest 最新的 v1.11 版本进行编译,并将生成的库文件和头文件输出到当前文件夹的 gtest 目录下,如下:

$ tree gtest
gtest
├── include
│   └── gtest
│       ├── gtest-death-test.h
│       ├── gtest.h
│       ├── gtest-matchers.h
│       ├── gtest-message.h
│       ├── gtest-param-test.h
│       ├── gtest_pred_impl.h
│       ├── gtest-printers.h
│       ├── gtest_prod.h
│       ├── gtest-spi.h
│       ├── gtest-test-part.h
│       ├── gtest-typed-test.h
│       └── internal
│           ├── custom
│           │   ├── gtest.h
│           │   ├── gtest-port.h
│           │   ├── gtest-printers.h
│           │   └── README.md
│           ├── gtest-death-test-internal.h
│           ├── gtest-filepath.h
│           ├── gtest-internal.h
│           ├── gtest-param-util.h
│           ├── gtest-port-arch.h
│           ├── gtest-port.h
│           ├── gtest-string.h
│           └── gtest-type-util.h
└── lib├── cmake│   └── GTest│       ├── GTestConfig.cmake│       ├── GTestConfigVersion.cmake│       ├── GTestTargets.cmake│       └── GTestTargets-noconfig.cmake├── libgtest.a├── libgtest_main.a└── pkgconfig├── gtest_main.pc└── gtest.pc
8 directories, 31 files
  • 目录 gtest/inlcude 包含 gtest 的头文件,写测试时需要包含头文件gtest/gtest.h
  • 目录 gtest/lib 包含 gtest 的库文件,链接时需要链接 libgtest.a 和 libgtest_main.a

特别说明:

由于是自动下载代码,所以需要 cmake 在 v3.14 以上;
gtest生成的两个库文件中,只有 libgtest.a 是必须的,另外一个库文件 libgtest_main.a是 gtest 运行的入口,实际上就是一个 main 函数,如果你在自己的单元测试中定义了自己的 main 函数去调用 gtest,那就不需要 libgtest_main.a。


2.2 编写测试代码


待测试文件
这里的测试文件有两个, 分别是 gcd.c 和 factorial.c。foo.h, 头文件,用于函数声明

#ifndef __FOO_H__
#define __FOO_H__
#ifdef __cplusplus
extern "C" 
{
#endifint gcd(int a, int b);int factorial(int n);#ifdef __cplusplus
}
#endif
#endif

gcd.c,用于计算两个数的最大公约数

#include "foo.h"int gcd(int a, int b)
{if (b == 0){return a;}else{return gcd(b, a % b);}
}

factorial.c,用于计算一个数的阶乘

#include "foo.h"int factorial(int n)
{int i, res;res = 1;for (i=n; i>0; i--){res *= i;}return res;
}

单元测试文件
写一个单元测试文件 foo_unittest.cc,包含两组测试,分别用于测试函数 gcd 和 factorial:
foo_unittest.cc

#include "foo.h"
#include <gtest/gtest.h>TEST(GCDTest, EvenTest)
{EXPECT_EQ(2, gcd(4, 10));EXPECT_EQ(6, gcd(30, 18));EXPECT_EQ(15, gcd(30, 45));
}TEST(GCDTest, PrimeTest)
{EXPECT_EQ(1, gcd(23, 10));EXPECT_EQ(1, gcd(359, 71));EXPECT_EQ(1, gcd(47, 83));
}TEST(FactorialTest, HandlesZeroInput) {EXPECT_EQ(factorial(0), 1);
}TEST(FactorialTest, HandlesPositiveInput) {EXPECT_EQ(factorial(1), 1);EXPECT_EQ(factorial(2), 2);EXPECT_EQ(factorial(3), 6);EXPECT_EQ(factorial(8), 40320);
}#if 0
int main(int argc, char *argv[])
{testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
#endif

在上面的代码中包含了两组一共4个测试:

一组 GCDTest, 用于测试 gcd 函数
一组 FactorialTest 用于测试 factorial 函数。


2.3 编译并运行测试


然后使用下面的语句编译上面的代码生成可执行的测试文件:

$ g++ gcd.c factorial.c foo_unittest.cc -o footest -I. -Igtest/include -Lgtest/lib -lgtest -lgtest_main -lpthread

注意: 这里使用 g++ 指令一口气编译了 gcd.c, factorial.c 和 foo_unittest.cc 一共 3 个文件,生成测试文件 footest。

执行测试:

参考上面的操作,实际上只需要将 g++ 指令中的代码文件替换成你实际的文件就可以了。

2.4 关于测试文件的说明
在 gcd.c, factorial.c 和 foo_unittest.cc 中都没有定义 main 函数。

只在 foo_unittest.cc 中,有一个注释掉的 main 函数:

#if 0
int main(int argc, char *argv[])
{testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
#endif


这个 main 函数是 GoogleTest 的入口,会调用 testing::InitGoogleTest(&argc, argv) 初始化 GoogleTest 框架,随后的 RUN_ALL_TESTS() 会搜集所有的测试函数,并运行。

实际上这也是 libtest_main.a 的所有代码,在 googletest v1.11 的代码中,完整的 gtest_main.cc 的代码如下:

/* file: googletest-src/googletest/src/gtest_main.cc */
#include <cstdio>
#include "gtest/gtest.h"#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
#if GTEST_OS_ESP8266
extern "C" {
#endif
void setup() {testing::InitGoogleTest();
}void loop() { RUN_ALL_TESTS(); }#if GTEST_OS_ESP8266
}
#endif#elseGTEST_API_ int main(int argc, char **argv) {printf("Running main() from %s\n", __FILE__);testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
#endif


在上面的代码中,针对 GTEST_OS_ESP8266 和 GTEST_OS_ESP32 的情形,单独定义了setup和loop函数,其余的代码都走 else 部分的 main 函数。

实际上 setup + loop 函数的功能和 main 函数是一样的,我猜测是 GTEST_OS_ESP8266 和 GTEST_OS_ESP32 默认有了 main 函数,所以这里无法再次定义 main 函数,于是将 googletest 的入口放入到 setup 和 loop 函数中。

可见,foo_unittest.cc的 main 函数 和 gtest_main.cc 的代码是一样的。

如果自己定义了 main 函数,就不再需要链接 libgtest_main.a 了,否则的话就需要向上面那样在编译时链接上 libgtest_main.a

3. 总结
基于前面说得比较繁琐,这里将步骤总结如下:

1:保存文中 2.1 节的 CMakeLists.txt 和 gtest.mk
2:运行 make -f gtest.mk 会自动下载并编译输出 gtest 的库文件和头文件
3:执行如下编译指令编译并链接测试代码和 gtest 库文件
$ g++ gcd.c factorial.c foo_unittest.cc -o footest -I. -Igtest/include -Lgtest/lib -lgtest -lgtest_main -lpthread

命令中的 gcd.c 和 factorial.c 可以根据需要替换成一个或多个 C 代码文件, foo_unittest.cc 替换成自己的单元测试文件(里面包含了多组 TEST 或 TEST_F测试)。


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/174805.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IPv6地址配置方式

IPv6地址分类 IPv6地址分为单播地址、任播地址&#xff08;Anycast Address&#xff09;、组播地址三种类型。和IPv4相比&#xff0c;取消了广播地址类型&#xff0c;以更丰富的组播地址代替&#xff0c;同时增加了任播地址类型。 单播地址 IPv6单播地址标识了一个接口&…

如何创建 Spring Boot 项目

如果有pom.xml有插件异常&#xff0c;可以先删除。 maven配置要配置好 然后yaml&#xff0c;再启动就行 server:port: 9991 spring:application:name: demo3参考 如何创建 Spring Boot 项目_创建springboot项目_良月初十♧的博客-CSDN博客

【多线程面试题十六】、谈谈ReentrantLock的实现原理

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;谈谈ReentrantLock的实现…

Android NDK开发详解之CMake工具链

Android NDK开发详解之CMake工具链 CMake 工具链文件用法工具链参数ANDROID_ABIANDROID_ARM_MODEANDROID_ARM_NEONANDROID_LDANDROID_NATIVE_API_LEVELANDROID_PLATFORMANDROID_STL 了解 CMake 构建命令使用预构建库构建第三方代码CMake 中的 YASM 支持报告问题 Android NDK 支…

本地生活商家用批量剪辑有用吗?

可以用&#xff0c;现在很多本地生活商家&#xff0c;都会通过借助批量剪辑工具来提升视频的曝光量&#xff0c;从而带动店铺的客流量。 推荐本地生活商家使用超级编导批量剪辑工具&#xff0c;这是一款0基础小白也可以很快上手的批量剪辑工具&#xff0c;剪辑页面布局以及功能…

c: two-dimensional array

/*** ****************************************************************************** file twoDimensional.h* brief 二维数组 Pointers and 2-D arrays* author geovindu,Geovin Du,涂聚文 (geovindu163.com)* ide: vscode c11,c17 windows 10* date…

VPS是什么?详解亚马逊云科技Amazon Lightsail(VPS)虚拟专用服务器

2006年&#xff0c;南非开普敦&#xff0c;亚马逊推出了WBS&#xff0c;以网络服务的形式向企业提供基础的IT服务。亚马逊云科技的一小步&#xff0c;在无数技术更迭&#xff0c;天才设计师和程序员的努力与基础设施建设的完善之下成为了人类科技进展的一大步。 亚马逊云科技可…

电路的电线的拼接

不积跬步无以至千里&#xff0c;今天小编也是复习今天学习的内容&#xff0c;废话不多说&#xff0c;看博客吧&#xff01;&#xff01;&#xff01; 目录 准备条件 操作 成品 准备条件 操作 将定制的套管插入导线当中&#xff0c;24V或者0V是尖端的端子&#xff0c;后面根…

web前端常见开发工具汇总 你用过几个?

搬运旗下公众号的内容~ 目录 1.记事本 2.Visual studio code 3.Hbuilder 4.Eclipse 5.Webstorm 6.Notepad 随着信息时代的不断进步&#xff0c;互联网在人类社会中所占的地位愈发举足轻重。大大小小的网站&#xff0c;构成了如今光怪陆离的网络社会。我们知道&#xff0c…

Sprint Cloud Stream整合RocketMq和websocket实现消息发布订阅

1.引入RocketMQ依赖&#xff1a;首先&#xff0c;在pom.xml文件中添加RocketMQ的依赖&#xff1a; <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.0</versi…

轻量级 IDE 文本编辑器 Geany 发布 2.0

Geany 是功能强大、稳定、轻量的开发者专用文本编辑器&#xff0c;支持 Linux、Windows 和 macOS&#xff0c;内置支持 50 多种编程语言。 2005 年Geany 发布首个版本 0.1。上周四刚好是 Geany 诞生 18 周年纪念日&#xff0c;官方发布了 2.0 正式版以表庆祝。 下载地址&#…

正则表达式引擎比较(翻译自:A comparison of regex engines)

原文&#xff1a; A comparison of regex engines – Rust Leipzig 引言 正则表达式&#xff08;或简称regex&#xff09;通常用于模式搜索算法。 有许多不同的正则表达式引擎提供不同的表达式支持、性能约束和语言绑定。 基于 John Maddock 之前的工作 (regex comparison)和…

window11 更改 vscode 插件目录,释放C盘内存

由于经常使用vscode开发会安装一些代码提示插件&#xff0c;然后C盘内容会逐渐缩小&#xff0c;最终排查定位到vscode。这个吃内存不眨眼的家伙。 建议&#xff1a;不要把插件目录和vscode安装目录放在同一个位置&#xff0c;不然这样vscode更新后&#xff0c;插件也会消失。 v…

Git窗口打开vim后如何退出编辑(IDEA/Goland等编辑器)

最近在学习git高级操作过程中&#xff0c;遇到了一下问题&#xff1a; 我在学习Git合并多个commit为一个的时候&#xff0c;需要输入一个命令 git rebase -i HEAD~2 这说明已经是编辑模式了。当我写好后&#xff0c;我还按照原来在linux上的按下ESC键&#xff0c;但是只是光…

【数据结构】数组和字符串(四):特殊矩阵的压缩存储:稀疏矩阵——三元组表

文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表结构体初始化元素设置打印矩阵主函数输出结果代码整合 4.2.1 矩阵的数组表示 【数据结构】数组和字符串&#xff08;一&#xff…

双11满减大促,直播间1折抢购!

你是知道的&#xff0c;双11本来是光棍节&#xff01; 2009年&#xff0c;阿里掀起了一场网络促销活动&#xff0c;光棍节从此成了全民的购物节&#xff01; 从2009年到2021年&#xff0c;阿里双11当天的交易额&#xff0c;从仅有的0.5亿猛增至5403亿&#xff0c;以惊人的速度…

目标检测及锚框、IoU

文章目录 1. 目标检测2. 锚框3. IoU - 交并比4. 赋予锚框标号5. 使用非极大值抑制&#xff08;NMS&#xff09;输出 1. 目标检测 物体检测&#xff08;目标检测&#xff09;是计算机视觉和数字图像处理的热门方向&#xff0c;意在判断一幅图像上是否存在感兴趣物体&#xff0c…

List的add(int index,E element)陷阱,不得不防

项目场景&#xff1a; 项目中有两个List列表&#xff0c;一个是List1用来存储一个标识&#xff0c;后续会根据这个标识去重。 一个List2是用来返回对象的&#xff0c;其中对象里也有一个属性List3。现需要将重复的标识数据追加到List3 我想到的两个方案&#xff1a; 尽量不动…

C/S架构和B/S架构

1. C/S架构和B/S架构简介 C/S 架构&#xff08;Client/Server Architecture&#xff09;和 B/S 架构&#xff08;Browser/Server Architecture&#xff09;是两种不同的软件架构模式&#xff0c;它们描述了客户端和服务器之间的关系以及数据交互的方式。 C/S 架构&#xff08…