目录
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]
扩展阅读
- C语言测试框架对比表
- 单元测试设计模式
选择框架时,建议优先考虑:熟悉度 > 项目需求 > 社区活跃度。就像选择汽车,超跑虽好,但日常通勤更适合经济型轿车。
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;
}
上面两个是常用的.基本上就是初级教程里面的内容,高级教程后面有链接.
初级教程
高级教程
关于汽车软件测试的拓展阅读: