【嵌入式单元测试】C语言单元测试框架搭建

article/2025/10/26 9:57:26

cmocka

    • cmocka交叉编译
      • 源码下载
    • 编译准备
      • 源码修改
      • 指定编译器
      • 编译
    • cmocka使用示例
    • 常见问题
    • 参考

单元测试框架是一个软件包,它能够让开发者比较方便的表达产品代码需要表现出什么样的行为。单元测试框架提供了一个自动化单元测试的解决方案,让开发者把更多的精力放在测试用例的设计的编写上,而不用花精力考虑如何对测试用例进行组织。

cmocka是一个优雅的C语言单元测试框架,支持模拟对象。它只需要标准的C库,适用于各种计算平台(Linux、windows,以及嵌入式)。
理论上来说,cmocka可以支持任何使用标准C库的交叉编译器。

本文将介绍如何在嵌入式环境(交叉编译)搭建cmocka单元测试环境,以及cmocka的简单使用示例。

cmocka交叉编译

源码下载

目前最新的1.1.5版本,对于嵌入式环境,我们需要下载源码进行交叉编译

cmocka1.1源码下载地址

这里以cmocka-1.1.5.tar.xz为例。

编译准备

将上述源码在linux环境中解压,并在源码同级目录新建编译目录build_dir

在这里插入图片描述

cmocka-1.1.5内容如下:

在这里插入图片描述

源码修改

进入cmocka-1.1.5源码目录,修改顶层CMakeLists.txt,将如下行注释掉。
doc组件需要特定的库支持,嵌入式环境一般没有这个库,而且这个只是生成代码注释,对功能没有影响,所以将其注释。

# add_subdirectory(doc)

指定编译器

接下来在build_dir目录的同级目录新建配置文件arm64_setup.cmake(文件名随意)
在这里插入图片描述
文件内容如下:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm64)set(tools /mnt/opt/compile_tools/bin/)
set(CMAKE_C_COMPILER ${tools}/aarch64-openwrt-linux-gcc)
set(CMAKE_CXX_COMPILER ${tools}/aarch64-openwrt-linux-g++)
  • CMAKE_SYSTEM_NAME 指定嵌入式系统类型
  • CMAKE_SYSTEM_PROCESSOR 指定嵌入式平台
  • tools 交叉编译器路径(以实际路径为准)
  • CMAKE_C_COMPILER C交叉编译器
  • CMAKE_CXX_COMPILER C++交叉编译器

编译

编译需要进入build_dir目录,先执行如下命令生成必要的配置和makefile文件

$ cd build_dir
$ cmake -DCMAKE_TOOLCHAIN_FILE=../arm64_setup.cmake -DBUILD_STATIC_LIB=ON ../cmocka-1.1.5/
  • -DCMAKE_TOOLCHAIN_FILE是指定刚刚创建的编译器配置文件
  • -DBUILD_STATIC_LIB=ON 是编译生成静态库,去掉这句只会生成动态库
  • ../cmocka-1.1.5/指定cmocka源码目录

如果上述步骤没有错误,那么在build_dir应该会生成若干目录和文件,其中就包括makefile,接下来执行make 编译即可。

$ make

出现下列提示表示编译成功:

Scanning dependencies of target cmocka
[  4%] Building C object src/CMakeFiles/cmocka.dir/cmocka.c.o
......
[100%] Linking C executable test_uptime
[100%] Built target test_uptime

如果需要clean,直接在build_dir目录执行make clean是不行的,因为Cmake不支持。最简单的方式就是把build_dir目录手动清空即可。

编译完成后查看build_dir/src,会生成我们需要的动态库或者静态库.
使用file指令可以看到这个动态库是ARM aarch64平台的,表明我们交叉编译成功了。

$ build_dir/src$ ls
CMakeFiles           libcmocka.so    libcmocka.so.0.7.0  Makefile
cmake_install.cmake  libcmocka.so.0  libcmocka-static.a$ file libcmocka.so.0.7.0 
libcmocka.so.0.7.0: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, with debug_info, not stripped

另外,build_dir/example目录下也会生成一些示例demo,可以直接在开发板上运行。

$ build_dir/example$ ls
allocate_module_test  CMakeFiles           Makefile
assert_macro_test     cmake_install.cmake  mock
assert_module_test    CTestTestfile.cmake  simple_test
$ file simple_test 
simple_test: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped

有了libcmocka,在加上源码中的cmocka.h,我就可以利用cmocka提供的API编译我们自己的单元测试代码了。

cmocka.h位于cmocka源码的include目录

cmocka使用示例

测试代码如下:

$ tree hello/ 
hello/
├── inc
│   └── cmocka.h
├── libs
│   ├── libcmocka.so
│   └── libcmocka-static.a
├── makefile
└── src└── hello_cmocka.c
  • libs文件夹主要存放库文件,推荐使用libcmocka静态库,使用静态库的好处是编译出的二进制文件在开发板上可以直接运行,如果是动态库还需要开发板上也安装这个动态库,静态库的缺点就是编译产物体积较大。

示例makefile文件如下:

#source file
SOURCE  += $(wildcard ./src/*.c)
OBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))TARGET  := hello_cmocka#compile and lib parameter
CC      := aarch64-openwrt-linux-gcc
LIBS    := -lcmocka-static
LDFLAGS := -L./libs  
DEFINES :=
INCLUDE := -I./inc/ 
CFLAGS  := -g -Wno-unused-variable -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) .PHONY: all : $(TARGET)
objs : $(OBJS)clean :rm -fr ./src/*.orm -fr $(TARGET)$(TARGET) : $(OBJS)$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)

示例hello_cmocka.c如下:

#include <stdarg.h>
#include <stdio.h>
#include <setjmp.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <cmocka.h>static int add(int a ,int b)
{return a+b;
}static char* print_string(int num)
{switch(num){case 1: return "CASE1";case 2: return "CASE2";default: return "NOT SUPPORT";}return "NOT SUPPORT";
}static void test_demo1(void **state)
{int ret = add(3,2); assert_int_equal(ret, 5);(void) state;
}
static void test_demo2(void **state)
{int ret = add(3,2);assert_int_equal(ret, 0);(void) state;
}static void test_demo3(void **state)
{char *p = print_string(1);assert_string_equal(p, "CASE1");(void) state;
}int main(void)
{const struct CMUnitTest tests[] = {cmocka_unit_test(test_demo1),cmocka_unit_test(test_demo2),cmocka_unit_test(test_demo3),};return cmocka_run_group_tests(tests, NULL, NULL);
}

运行结果如下:

# ./hello_cmocka
[==========] Running 3 test(s).
[ RUN      ] test_demo1
[       OK ] test_demo1
[ RUN      ] test_demo2
[  ERROR   ] --- 0x5 != 0
[   LINE   ] --- src/hello_cmocka.c:46: error: Failure!
[  FAILED  ] test_demo2
[ RUN      ] test_demo3
[       OK ] test_demo3
[==========] 3 test(s) run.
[  PASSED  ] 2 test(s).
[  FAILED  ] 1 test(s), listed below:
[  FAILED  ] test_demo21 FAILED TEST(S)

提示test_demo2测试失败,原因是0x5 != 0,test_demo2的预期结果是0,实际返回5,不符合预期,故测试失败。
assert_int_equal()是判断int类型结果,assert_string_equal()是判断const char *类型的结果。除此之外,cmocka还提供了更多其他的测试API,请参考cmocka.h.

常见问题

  1. 编译错误
/cmocka-1.1.5/include/cmocka.h:132:28: error: conflicting types for 'uintptr_t'
typedef unsigned int uintptr_t;
/mnt/opt/include/bits/alltypes.h:109:24: note: previous declaration of 'uintptr_t' was heretypedef unsigned _Addr uintptr_t;

cmocka源码中uintptr_t定义和我们的交叉编译器中的定义冲突了,这里只需要暂时把编译器中的定义注释掉,编译完cmocka再改回来即可。

  1. 其他编译问题

cmocka仅使用了标准C库,它的跨平台兼容性很好。因此,对于绝大多数的交叉编译器应该都是支持的(除非你的编译器版本很低,对C库的支持不全)。
因此,交叉编译cmocka源码中遇到的编译问题基本上都是C代码问题,与平台无关。

参考

  • cmocka官网
  • cmocka API介绍

http://chatgpt.dhexx.cn/article/MkmLswT4.shtml

相关文章

三年黑盒测试工程师,带你了解嵌入式测试,金三银四升职加薪秘诀

什么是嵌入式系统? 嵌入式系统是一种“完全嵌入受控器件内部,为特定应用而设计的专用计算机系统”。 嵌入式系统是“用于控制,监视或辅助操作机器和设备的装置”。 嵌入式系统还可以定义为“以应用为中心,以计算机技术为基础,软硬件可裁剪,功能、可靠性、成本、体积、功耗…

嵌入式软件测试的小结

文章内容为本人这三年来在嵌入式软件测试&#xff08;黑盒&#xff09;上的一些积累吧&#xff0c;说起来也挺快的&#xff0c;毕业三年的时间就这样过去了&#xff0c;在两家公司工作过&#xff08;现在这家是第二家&#xff09;&#xff0c;这几年的测试项目基本都是围绕着嵌…

【测试】嵌入式软件测试VS一般软件测试

文章目录 1&#xff09;什么是软件测试&#xff1f;测试的目的&#xff1a;软件测试的特点&#xff1a;软件测试信息流&#xff1a;软件测试的对象&#xff1a; 2&#xff09;嵌入式软件测试2.1 嵌入式软件2.2 嵌入式软件测试嵌入式软件测试的特点&#xff1a; 3&#xff09;嵌…

嵌入式软件自动化测试介绍

什么是嵌入式测试 嵌入式软件测试的概念似乎没那么大众&#xff0c;很多人从字面上理解&#xff0c;可能会以为这是个硬件测试&#xff0c;那么嵌入式测试实际上是什么呢&#xff1f; 根据IEEE&#xff08;国际电机工程师协会&#xff09;的定义&#xff0c;嵌入式系统是“控…

嵌入式软件测试的基本方法

嵌入式系统是以应用为中心&#xff0c;以计算机技术为基础&#xff0c;软件硬件可剪裁&#xff0c;适应应用系统对功能、可靠性、成本、体积及功耗严格要求的专用计算机系统。嵌入式系统的软硬件功能界限模糊&#xff0c;测试比PC系统软件测试要困难得多&#xff0c;嵌入式软件…

嵌入式测试大赛预选赛

刚刚参加了预选赛&#xff0c;对于这种热身赛&#xff0c;是不需要一点编程能力的&#xff0c;只不过需要一些细心 题目下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Xm2d8UYhrK75fukcXQhEcg 密码&#xff1a;hxzy 这次预选赛是在练习题4的基础上改的&#x…

嵌入式软件测试

如何在目标板上实时测试应用程序为什么嵌入式系统测试困难&#xff1f; 在目标板上测试面临的系列问题&#xff1a; 1、如何下载测试到板子上&#xff0c;然后如何收集测试结果 2、如何累积可重复自动执行的测试 3、如何尽可能减少人工工作 4、如何减少内存不够的问题 这…

全国软件测试大赛嵌入式测试步骤及所需工具

文章目录 前言一、所需工具二、测试步骤1.从慕测平台上下载题目2.搭建测试环境3.测试脚本编写怎么编写 总结 前言 全国软件测试大赛嵌入式测试最全步骤及所需的工具 一、所需工具 若需要测试工具请私信我 二、测试步骤 以2019年的省赛题目为例 1.从慕测平台上下载题目 下…

嵌入式软件测试(黑盒测试)-----三年嵌入式软件测试的理解

前言 文章内容为本人这三年来在嵌入式软件测试&#xff08;黑盒&#xff09;上的一些积累吧&#xff0c;说起来也挺快的&#xff0c;毕业三年的时间就这样过去了&#xff0c;在两家公司工作过&#xff08;现在这家是第二家&#xff09;&#xff0c;这几年的测试项目基本都是围绕…

简单聊聊嵌入式软件测试

一、什么是嵌入式&#xff1f;什么是嵌入式软件测试&#xff1f; 此文不从行业术语来讲&#xff0c;就用大白话来描述&#xff0c;容易明白&#xff0c;不当之处&#xff0c;还请见谅和指正。 嵌入式&#xff1f;简单的可以理解为上位机或者单片机&#xff0c;或者运行微型可定…

嵌入式测试

一、嵌入式软件测试的方法 嵌入式软件测试分为4个阶段&#xff0c;即模块测试、集成测试、系统测试、硬件/软件集成测试。前3个阶段适用于任何软件的测试&#xff0c;硬件/软件集成测试阶段是嵌入式软件所特有的&#xff0c;目的是验证嵌入式软件与其所控制的硬件设备能否正确地…

机器学习必备知识点 之 先验概率和后验概率

机器学习必备知识点 见 机器学习必备知识点 我们可以把概率获得分为两种&#xff1a; 一种是从原因到结果——先验概率一种是从结果到原因——后验概率 举个例子&#xff1a; 这里的P(C1)&#xff0c;P(C2)&#xff0c;P(x|C1)&#xff0c;P(x|C1)都是先验概率&#xff0c;因…

Gretna网络分析之先验知识

目录 1. 简介 1.1 小世界网络 1.2 平均路径长度 2.功能键介绍 2.1 Global Network metrics 2.2 Nodal and modular network metrics 3.彩蛋 &#xff08;转载请注明来自Ressan博客&#xff09; 1. 简介 网络&#xff1a;由节点和连线构成的图/模型&#xff0c;用来研究…

如何将先验知识注入推荐模型

看到知乎上的一个问题“如何向深度学习模型中加入先验知识&#xff1f;”&#xff0c;觉得这是一个很好的问题&#xff0c;恰好自己在这方面有一些心得&#xff0c;今天拿出来和大家聊一聊。 说这个问题有趣&#xff0c;是因为提问者一定是对DNN的“智能”程度不满意了&#xf…

如何给模型加入先验知识?

来源&#xff1a;PaperWeekly 作者&#xff1a;Billy Z 模型加入先验知识的必要性 端到端的深度神经网络是个黑盒子&#xff0c;虽然能够自动学习到一些可区分度好的特征&#xff0c;但是往往会拟合到一些非重要特征&#xff0c;导致模型会局部坍塌到一些不好的特征上面。常常一…

几何深度学习 - 利用几何先验知识的深度学习

深度学习很难。 虽然通用逼近定理表明足够复杂的神经网络原则上可以逼近“任何东西”&#xff0c;但不能保证我们可以找到好的模型。 尽管如此&#xff0c;通过明智地选择模型架构&#xff0c;深度学习取得了巨大进步。 这些模型架构对归纳偏差进行编码&#xff0c;为模型提供…

如何向深度学习模型中加入先验知识?

链接&#xff1a;https://www.zhihu.com/question/279012198 编辑&#xff1a;深度学习与计算机视觉 声明&#xff1a;仅做学术分享&#xff0c;侵删 作者&#xff1a;采石工https://www.zhihu.com/question/279012198/answer/1298985606 1) CNN 中使用的卷积层就是利用了图像像…

浅谈模型中加入先验知识

必要性 端到端的神经网络是个黑盒&#xff0c;虽然能够学习到一些可区分度好的特征&#xff0c;但是也会拟合到一些非重要特征&#xff0c;导致模型局部地区会有一些不好的特征表现&#xff0c;所以可以加入一些先验知识来优化这些区域表现。 首先给出可以有效的加入先验信息的…

综述:如何给模型加入先验知识

转载自&#xff1a;https://zhuanlan.zhihu.com/p/188572028 这里写目录标题 为什么要给模型加入先验知识&#xff1f;1 基于pretrain模型给模型加入先验2 基于输入给模型加入先验3 基于模型重现给模型加入先验4 基于CAM图激活限制给模型加入先验 为什么要给模型加入先验知识&a…