[测试] Google Test | 主流的 C 测试框架

news2025/4/25 20:14:30

目录

GoogleTest

2. 准备工作

3. 测试

4.怎么用


Attention is All You Need

写项目代码的时候 边写边测 非常重要,这样可以帮助我们减少很多的问题。

  • 这篇文章后面 主要以 GoogleTest 为例,进行介绍
  • 最近找了些 gtest 相关的资料,学习了下.后面主要记录了学习过程和相关知识点.

参考

初级教程
高级教程


主流框架

一、轻量级嵌入式框架(适合单片机开发)

1. SCUNIT —— 嵌入式系统的"微型工具箱"
  • 特点:专为资源受限的嵌入式环境设计,核心代码仅需标准C支持,无需依赖外部库 。
  • 使用场景:STM32等单片机开发,路由表、驱动模块测试。
  • 使用方法
#include "scunit.h"
void test_case() {
    SCUNIT_ASSERT(1+1 == 2, "算术测试");  // 类似检查螺丝刀是否拧紧
}
int main() {
    scunit_add_suite("基础测试");
    scunit_add_test("算术运算", test_case);
    scunit_run();  // 启动测试流水线
}
  • 源码地址:SCUNIT博客介绍

2. EmbedUnit —— 嵌入式开发的"机械臂校准仪"
  • 特点:完全静态内存分配,不依赖任何标准库,适合裸机开发
  • 使用场景:无操作系统的微控制器测试。
  • 测试示例
TEST_ASSERT_MESSAGE(adc_value > 1000, "电压阈值检测"); // 如同检测传感器信号强度
  • 项目地址:EmbedUnit官网

二、通用型框架(适合PC/服务器开发)

3. Check —— C语言的"安全气囊测试系统"
  • 特点:每个测试用例运行在独立子进程,避免段错误导致整体崩溃
  • 使用场景:指针操作密集的底层模块测试。
  • 安装使用
# Ubuntu安装
sudo apt-get install check
# 编写测试用例(如测试链表操作)
tcase_add_test(tc_core, test_list_insert);  // 类似模拟碰撞测试
  • 官网文档:Check官方指南

4. CUnit —— 企业级测试的"自动化流水线"
  • 特点:支持XML输出、测试套件管理,适合大型项目
  • 使用场景:持续集成环境中的回归测试。
  • 示例配置
CU_add_test(suite, "数据库连接测试", test_db_connect);  // 如同质检站的多个检测环节
CU_basic_run_tests();  // 启动流水线
  • 下载地址:CUnit官网

三、极简框架(适合快速验证)

5. CuTest —— 程序员的"瑞士军刀"
  • 特点:仅需两个文件(CuTest.h + CuTest.c),代码量<1000行
  • 使用场景:算法验证、教学演示。
  • 快速上手
CuSuite* suite = CuSuiteNew();
SUITE_ADD_TEST(suite, TestStringUtils);  // 如同随身工具包中的多功能刀
CuSuiteRun(suite);
  • 源码获取:CuTest GitHub镜像

6. Unity + CMocka —— 模块化开发的"乐高积木组合"
  • 组合优势
    • Unity:断言库丰富,支持内存泄漏检测
    • CMocka:提供桩函数生成,模拟外部依赖
  • 典型应用
TEST_FUNCTION(memory_alloc_test) {
    void* ptr = malloc(100);
    TEST_ASSERT_NOT_NULL(ptr);  // 类似检查积木拼接是否牢固
}
  • 项目资源:CMocka官网

四、跨语言框架(C/C++混合项目)

7. Google Test —— 工业级项目的"全身体检仪"
  • 特点:源自Google的测试框架,支持死亡测试、参数化测试
  • 适用场景:混合C/C++项目,如游戏引擎开发。
  • 安装示例
git clone https://github.com/google/googletest
cd googletest && mkdir build && cd build
cmake .. && make  # 编译生成库文件
  • 测试用例:
TEST(CalcTest, AddTest) {
    EXPECT_EQ(add(2,3), 5);  // 如同X光检测骨骼强度
}

框架选择决策树

id: test-framework-choice
name: 测试框架选择指南
type: mermaid
content: |-
  graph TD
    A[项目类型?] -->|嵌入式| B{资源限制?}
    B -->|RAM<8KB| C[SCUNIT/EmbedUnit]
    B -->|资源充足| D[Check]
    A -->|PC/服务器| E{是否需要高级功能?}
    E -->|是| F[GoogleTest/CUnit]
    E -->|否| G[CuTest]
    A -->|混合C/C++| H[GoogleTest]
    A -->|模块隔离测试| I[Unity+CMocka]

扩展阅读

  1. C语言测试框架对比表
  2. 单元测试设计模式

选择框架时,建议优先考虑:熟悉度 > 项目需求 > 社区活跃度。就像选择汽车,超跑虽好,但日常通勤更适合经济型轿车。


GoogleTest

最近,找了些GoogleTest相关的资料,学习了下.这里主要记录了学习过程和相关知识点.

1. 什么是gtest

  • gtest测试框架是在不同平台上(Linux,Mac OS X,Windows,Cygwin,Windows CE和Symbian)为 编写C++测试而生成的。
  • 它是基于xUnit架构的测试框架,支持自动发现测试
  • 丰富的断言集,用户定义的断言.

关于断言,前年在学 C 语言时,有记录过 15.C与指针--超全总结

2. 准备工作

获取gtest源码: https://github.com/google/googletest.git

在shell下:

cd  googletest/
cmake ..
make

成功后得到静态库文件:

将libgtest.a  libgtest_main.a拷贝到main.cpp同级目录下(main.cpp自己创建)
cp googletest/bulid/lib/libgtest*.a .

3. 测试

先搞个例子跑起来.
main.cpp

/* main.cpp
 * Created by lvy on 2025/4/1.
 */
#include "gtest/gtest.h"
#include <iostream>
#include <string>

int add(int a, int b)
{
	return a + b;
}

TEST(fun, add_a)
{
	EXPECT_EQ(-3, add(-2,-1));
	EXPECT_EQ(-2, add(1,-3));
}

int main(int argc, char **argv){

    ::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();

	return 0;
}

关于库的一些详细信息,我们也可以跳转进行查看

哦对,还发现了一个看注释的翻译插件挺好用的:

可以更方便的看源码了

编译:

g++ main.cpp  libgtest.a -lpthread -std=c++17 -I /home/lvy/OS_AI/gtest/goo
gletest/googletest/include -o m
(由于gtest版本问题,12.x  必须c++17以上才能编译通过,刚开始编译用的11标准,一直报错)
(有用的就是静态库和头文件,将来移植的时候就是需要这两个东西)

还是 那句话,相信机器永远是对的~

这样就算可以用啦.后面具体讲一下,gtest怎么用.


4.怎么用

断言

ASSERT_XXX 和 EXPECT_XXX 两类.

区别:

  • 如果 ASSERT_XXX 测试结果不通过, 后面的测试就不会执行了.
  • 如果 EXPECT_XXX 测试结果不通过, 后面的测试会接着执行.

布尔断言:单参断言

  • ASSERT_TRUE、ASSERT_FALSE、EXPECT_TRUE、EXPECT_FALSE

数值断言:双参

  • ASSERT_EQ、ASSERT_NE、ASSERT_LT、ASSERT_LE、ASSERT_GT、ASSERT_GE
  • EXPECT_EQ、EXPECT_NE、EXPECT_LT、EXPECT_LE、EXPECT_GT、EXPECT_GE

字符串断言

  • ASSERT_STREQ、ASSERT_STRCASEEQ

TEST(test_suite_name,test_name)

  • 一个TEST()算是一个测试case.
  • TEST(x,y)展开为x_y_TEST()这样的函数

比如测试add()函数,我们可以考虑多种测试情况: 和为负数, 和为正数, 极限值测试.

/* main.cpp
 * Created by lvy on 2025/4/1.
 */
#include "gtest/gtest.h"
#include <iostream>
#include <string>

int add(int a, int b)
{
	return a + b;
}

TEST(add, negative)
{
	EXPECT_EQ(-3, add(-2,-1));
	EXPECT_EQ(-2, add(1,-3));
}

TEST(add, positive)
{
	EXPECT_EQ(1, add(2,-1));
	EXPECT_EQ(2, add(-1,3));
}

TEST(add, limit)
{
    int a = 0x7fffffff + 1;
    std::cout<<"a = "<<a<<"\n";
	EXPECT_EQ(a, add(0x7fffffff,1));
	EXPECT_EQ(0, add(0xffffffff,1));
}

int main(int argc, char **argv){

    ::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();

	return 0;
}


TEST_F(x,y)

以下函数使用需要定义测试套类继承::testing::Test,重新实现对应函数.

  • Set UpTest Suite:static测试套级别,运行测试套 第一个用例前执
  • Tear DownTest Suite: static 测试套级别,运行测试套 最后一个用例后执行
  • Set Up:virtual 测试套中每个测试用例 开始时执行
  • Tear Down:virtrual 测试套中每个测试用例运行后执行

x 为 class名.

[怎么理解,举个例子:

  • TEST()适用于单次api测试,但是通常我们都是以模块或者功能进行开发的
  • 比如我们写了一个链表,链表里面有很多api:添加,删除,比较,等.

对于整个链表的测试就是套件级别的,里面的api就是test级别的.

  • TEST_F(list, add)
  • TEST_F(list, delete)
/* main.cpp
 * Created by lvy on 2025/4/1.
 */
#include "gtest/gtest.h"
#include <iostream>
#include <string>

int add(int a, int b)
{
	return a + b;
}

TEST(add, negative)
{
	EXPECT_EQ(-3, add(-2,-1));
	EXPECT_EQ(-2, add(1,-3));
}

TEST(add, positive)
{
	EXPECT_EQ(1, add(2,-1));
	EXPECT_EQ(2, add(-1,3));
}

TEST(add, limit)
{
    int a = 0x7fffffff + 1;
    std::cout<<"a = "<<a<<"\n";
	EXPECT_EQ(a, add(0x7fffffff,1));
	EXPECT_EQ(0, add(0xffffffff,1));
}

class FooTest : public ::testing::Test {
 protected:
  // You can remove any or all of the following functions if their bodies would
  // be empty.

  FooTest() {
     // You can do set-up work for each test here.
  }

  ~FooTest() override {
     // You can do clean-up work that doesn't throw exceptions here.
  }
  static void SetUpTestSuite() {
    std::cout<<"===================run before first case..."<<std::endl;
  } 

  static void TearDownTestSuite() {
    std::cout<<"===================run after last case..."<<std::endl;
  }
  // If the constructor and destructor are not enough for setting up
  // and cleaning up each test, you can define the following methods:

  void SetUp() override {
    std::cout<<" =========================SetUp() \n";
     // Code here will be called immediately after the constructor (right
     // before each test).
  }

  void TearDown() override {
    std::cout<<" =========================TearDown() \n";
     // Code here will be called immediately after each test (right
     // before the destructor).
  }

  // Class members declared here can be used by all tests in the test suite
  // for Foo.
};

TEST_F(FooTest,test_a)
{
  EXPECT_EQ(2, add(0x7fffffff,1));
}

TEST_F(FooTest,test_b)
{
  EXPECT_EQ(1, add(0,1));
}

int main(int argc, char **argv){

  ::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();

	return 0;
}

上面两个是常用的.基本上就是初级教程里面的内容,高级教程后面有链接.

初级教程
高级教程

关于汽车软件测试的拓展阅读:

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV 图形API(3)高层次设计概览

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 描述 G-API 是一个异构框架&#xff0c;提供了统一的 API 来使用多个支持的后端编程图像处理流水线。 关键的设计理念是在指定使用哪些内核和设备时保持流…

碰一碰发视频网页版本开发的源码搭建指南

引言 在数字化信息快速传播的时代&#xff0c;近场通信&#xff08;NFC&#xff09;技术为信息交互带来了新的便捷方式。通过网页版本实现碰一碰发视频功能&#xff0c;能够让用户在浏览器环境中轻松实现视频分享&#xff0c;拓展了视频传播的途径。本文将详细介绍碰一碰发视频…

【含文档+PPT+源码】基于Python爬虫二手房价格预测与可视化系统的设计与实现

项目介绍 本课程演示的是一款基于Python爬虫二手房价格预测与可视化系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 带你从零开始部署运行本套系统 该项…

Redis-16.在Java中操作Redis-Spring Data Redis使用方式-操作有序集合类型的数据

一. 操作有序集合类型的数据 package com.sky.test;import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.*;imp…

STM32智能手表——任务线程部分

RTOS和LVGL我没学过&#xff0c;但是应该能硬啃这个项目例程 ├─Application/User/Tasks # 用于存放任务线程的函数 │ ├─user_TaskInit.c # 初始化任务 │ ├─user_HardwareInitTask.c # 硬件初始化任务 │ ├─user_RunModeTasks.c…

SQL命令

一、表的创建 SQL MS Access、MySQL 和 SQL Server 数据类型 | 菜鸟教程 SQL Server 和 MySQL 中的 Date 函数 | 菜鸟教程 1.1、创建表 CREATE TABLE Citys (CityID int PRIMARY KEY,CityName varchar(255) );CREATE TABLE Per (PersonID int PRIMARY KEY, …

终端SSH连接工具SecureCRT安装和连接Linux

SecureCRT 9.5是一款集终端仿真与加密功能于一身的专业软件&#xff0c;其坚如磐石的安全性、高效的信息传输能力以及高度可定制的会话管理&#xff0c;使得它成为众多用户的首选。该软件不仅支持SSH2、SSH1、Telnet等多种协议&#xff0c;还提供了Relogin、Serial、TAPI、RAW等…

赛逸展2025“创新引擎”启动:限量席位,点亮科技绿色新征程

当今时代&#xff0c;科技革新与绿色发展已然成为推动社会进步的双引擎。2025第七届亚洲消费电子技术贸易展&#xff08;赛逸展&#xff09;敏锐捕捉这一趋势&#xff0c;重磅打造“科技创新专区”&#xff0c;并面向科技、绿色企业吹响限量招募号角。 这个独具特色的专区紧扣…

FPGA实现数码管显示分秒时间

目录 一. verilog实现 二. 烧录验证 三. 结果验证 使用开发板&#xff1a;DE2-115开发板 一. verilog实现 要实现分和秒&#xff0c;需要知道定时器的频率&#xff0c;通过查手册可知&#xff0c;我使用的开发板时钟为50hz&#xff0c;也就是时钟一个周期是2微秒。 5000000…

可视化开发:用Qt实现Excel级动态柱状图

Qt柱状图 QtChart 首先我们介绍一下 图表建立的基础&#xff1a;Qt Charts QtChart 是Qt框架的一个模块&#xff0c;专注与提供交互式数据可视化功能 俗话就是 用于用户轻松创建各种类型的图表和图形界面 它包含的图表类型有很多&#xff1a;折线图&#xff0c;饼图&#x…

从零实现Json-Rpc框架】- 项目实现 - 基于Dispatcher模块的RPC框架

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…

kubekey -实现懒人一键部署K8S集群

kubekey -实现懒人一键部署K8S集群 操作步骤 官网&#xff1a; https://kubesphere.io/zh/ 一、执行以下命令快速创建一个 Kubernetes 集群。 Master节点 如果您访问 GitHub/Googleapis 受限&#xff0c;请登录 Linux 主机&#xff0c;执行以下命令设置下载区域。 [roottest ~]…

李宏毅机器学习笔记(1)—机器学习基本概念+深度学习基本概念

机器学习基本概念 1、获取模型 步骤 1.1、假定未知函数 带未知参数的函数 1.2、定义损失函数 真实值&#xff1a;label MAE MSE 几率分布&#xff0c;cross-entropy? 1.3、优化 单独考虑一个参数 让损失函数最小&#xff0c;找导数为零的点 单独考虑w&#xff0c;w…

数字IC后端项目常见问题之streamOut layermap和innovus drc violation

Q1&#xff1a;我需要将Innovus设计GDS导出到Virtuoso&#xff0c;但发现写出GDS的过程会报如下所示的警告。这里写出GDS使用的是Virtuoso (DFII) streamOut mapping文件&#xff01; Clock Gen模块Routing DRC&#xff0c;Timing分析及解决 streamOut tease.gds2 -mapFile cd…

短剧系统开发动漫短剧系统源码开发上线小程序app教程

一、市场规模与用户增长&#xff1a;突破677亿&#xff0c;Z世代成主力 整体扩张 2025年短剧市场预计同比增长15%&#xff0c;规模达677.9亿元&#xff0c;用户规模6.62亿&#xff08;占网民59.7%&#xff09;。动漫短剧作为细分领域&#xff0c;增速显著受益于二次元文化渗透&…

太阳能高杆路灯:照亮未来的新光

在全球能源转型进程加速以及可持续发展理念日益深入人心的背景下&#xff0c;太阳能高杆路灯作为融合新能源技术、智能控制技术与多功能集成特性的创新产品&#xff0c;正逐步革新传统路灯的格局。其不仅有效解决了传统路灯对电网供电的依赖问题&#xff0c;更为城市及乡村的照…

《C++Linux编程进阶:从0实现muduo 》-第8讲.C++面试如何高效获取线程ID

章节重点 在C面试时&#xff0c;经常被问到如果高效获取线程ID&#xff0c;但不少同学都不知道如何回答。 重点是通过__thread关键字。 重点内容 视频讲解&#xff1a;《CLinux编程进阶&#xff1a;从0实现muduo C网络框架系列》-第8讲. C面试如何高效获取线程ID 测试获取线…

【Tauri2】011——菜单menu(2)

前言 前面简单地创建了菜单&#xff0c;接下来就来试试菜单中的action Rust中菜单项注册action AppHandle in tauri - Rusthttps://docs.rs/tauri/2.4.0/tauri/struct.AppHandle.html#method.on_menu_event这就需要用到App或者AppHandle中的方法on_menu_event #[must_use] …

架构设计基础系列:面向对象设计的原则

引言 面向对象设计&#xff08;Object-Oriented Design&#xff0c;OOD&#xff09;是软件开发中的重要概念&#xff0c;其核心在于通过对象、类、继承、封装和多态等机制&#xff0c;实现对现实世界问题的抽象和建模。OOD不仅有助于提高代码的可重用性、可维护性和可扩展性&a…

UE5学习笔记 FPS游戏制作35 使用.csv配置文件

文章目录 导入.csv要求首先创建一个结构体导入配置文件读取配置 导入 .csv要求 第一行必须包含标题 第一列的内容必须不能重复&#xff0c;因为第一列会被当成行的名字&#xff0c;在数据处理中发挥类似于字典的key的作用 当前的配置文件内容如下 首先创建一个结构体 结构…