一个简单好用的C语言单元测试框架-Unity
Unity简介:
Unity是一个用于C语言的轻量级单元测试框架。它由Throw The Switch团队开发,旨在简化嵌入式系统的单元测试。单元测试中单元的含义,单元就是人为规定的最小的被测功能模块,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。在实际项目中,单元测试往往由开发人员完成。
Unity的设计目标是易于使用、轻便、可移植,并能够在各种嵌入式和非嵌入式系统中运行。核心项目是一个 C 文件和一对头文件,允许将其添加到现有的构建设置中,而不会太麻烦。 可以使用任何想用的编译器,并且可以使用大多数现有的构建系统,包括 Make、CMake 等。
Unity简单使用方法:
1、下载unity框架
去GitHub下载,或者gti clone到本地,链接地址:unity。
Unity 本身非常小。 其他剩下的只是可选附加组件。 可以忽略它或在方便时使用它。 以下是项目中所有内容的概述。
src- 这是你关心的代码!此文件夹包含一个 C 文件和两个头文件。 这三个文件是 Unity。docs- 在这里可以找到所有方便的文档。examples- 这包含一些使用 Unity 的示例。extras- 这些是 Unity 的可选附加组件,不属于核心项目。test- 这就是 Unity 及其脚本的测试方式。 如果你只是使用Unity,你可能永远不需要进入这里。auto- 在这里,你将找到有用的 Ruby 脚本,以简化你的测试工作流程。 它们完全是可选的,不需要使用 Unity。
2、使用unity框架
测试文件是 C 文件。 大多数情况下,将为每个要测试的 C 模块创建一个测试文件。 测试文件应包含 unity.h 和要测试的 C 模块的标头。
例如测试下面两个函数:
ProductionCode.c
#include "ProductionCode.h"int Counter = 0;
int NumbersToFind[9] = { 0, 34, 55, 66, 32, 11, 1, 77, 888 }; /* some obnoxious array to search that is 1-based indexing instead of 0. *//* This function is supposed to search through NumbersToFind and find a particular number.* If it finds it, the index is returned. Otherwise 0 is returned which sorta makes sense since* NumbersToFind is indexed from 1. Unfortunately it's broken* (and should therefore be caught by our tests) */
int FindFunction_WhichIsBroken(int NumberToFind)
{int i = 0;while (i < 8) /* Notice I should have been in braces */i++;if (NumbersToFind[i] == NumberToFind) /* Yikes! I'm getting run after the loop finishes instead of during it! */return i;return 0;
}int FunctionWhichReturnsLocalVariable(void)
{return Counter;
}
ProductionCode.h
#ifndef _PRODICTIONCODE_H_
#define _PRODICTIONCODE_H_extern int Counter;int FindFunction_WhichIsBroken(int NumberToFind);
int FunctionWhichReturnsLocalVariable(void);#endif
接下来,测试文件将包含测试例程函数。 setUp 函数可以包含你希望在每次测试之前运行的任何内容。 tearDown 函数可以包含你希望在每次测试后运行的任何内容。 这两个函数都不接受任何参数,也不返回任何内容。 如果不需要它们,将其设置为空。setUp() tearDown()
TestProductionCode.h
#include "ProductionCode.h"
#include "unity.h"/* sometimes you may want to get at local data in a module.* for example: If you plan to pass by reference, this could be useful* however, it should often be avoided */
extern int Counter;void setUp(void)
{/* This is run before EACH TEST */Counter = 0x5a5a;
}void tearDown(void)
{
}void test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode(void)
{/* All of these should pass */TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(78));TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(2));TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(33));TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(999));TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(-1));
}void test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken(void)
{/* You should see this line fail in your test summary */TEST_ASSERT_EQUAL(1, FindFunction_WhichIsBroken(34));/* Notice the rest of these didn't get a chance to run because the line above failed.* Unit tests abort each test function on the first sign of trouble.* Then NEXT test function runs as normal. */TEST_ASSERT_EQUAL(8, FindFunction_WhichIsBroken(8888));
}void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue(void)
{/* This should be true because setUp set this up for us before this test */TEST_ASSERT_EQUAL_HEX(0x5a5a, FunctionWhichReturnsLocalVariable());/* This should be true because we can still change our answer */Counter = 0x1234;TEST_ASSERT_EQUAL_HEX(0x1234, FunctionWhichReturnsLocalVariable());
}void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain(void)
{/* This should be true again because setup was rerun before this test (and after we changed it to 0x1234) */TEST_ASSERT_EQUAL_HEX(0x5a5a, FunctionWhichReturnsLocalVariable());
}void test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed(void)
{/* Sometimes you get the test wrong. When that happens, you get a failure too... and a quick look should tell* you what actually happened...which in this case was a failure to setup the initial condition. */TEST_ASSERT_EQUAL_HEX(0x1234, FunctionWhichReturnsLocalVariable());
}
最后,在编写main函数,然后调用每个测试例程函数。 这实际上会触发每个测试例程函数运行,因此每个函数都有自己的调用非常重要。
/* AUTOGENERATED FILE. DO NOT EDIT. */
/*=======Automagically Detected Files To Include=====*/
#include "unity.h"
#include <setjmp.h>
#include <stdio.h>
#include "ProductionCode.h"/*=======External Functions This Runner Calls=====*/
extern void setUp(void);
extern void tearDown(void);
extern void test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode(void);
extern void test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken(void);
extern void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue(void);
extern void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain(void);
extern void test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed(void);/*=======MAIN=====*/
int main(void)
{UnityBegin("test/TestProductionCode.c");RUN_TEST(test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode);RUN_TEST(test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken);RUN_TEST(test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue);RUN_TEST(test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain);RUN_TEST(test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed);return (UnityEnd());
}
3、构建运行环境
创建一个目录 TDDUnityExample,在TDDUnityExample 目录下创建 CMakeLists.txt 文件。
# 最低CMake版本要求
cmake_minimum_required(VERSION 3.15)#将Unity/src工作目录的源文件赋给UNITY_SRC_LIST
#将Tests工作目录的源文件赋给APP_SRC_DIR
aux_source_directory (Unity/src UNITY_SRC_LIST)
aux_source_directory (Tests APP_SRC_DIR)# 项目名称
project(TDD_test)# 头文件路径
include_directories(Unity/src)#将所有源文件生成一个可执行文件
add_executable(TDD_test ${APP_SRC_DIR} ${UNITY_SRC_LIST})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
在 TDDUnityExample 下创建 Unity 目录,然后将Unity 框架源码中的 src 目录拷贝到 Unity目录中。
在 TDDUnityExample 下创建 Tests 目录,然后将第二点中举例的测试文件都放在 Tests 目录中。
在 TDDUnityExample 下创建 build 目录,然后在 build 目录下也创建一个 CMakeLists.txt 文件。
cmake_minimum_required (VERSION 2.8)project (TDD_test)add_subdirectory (Tests)
最后整体文件结构如下:
├── build
│ └── CMakeLists.txt
├── CMakeLists.txt
├── Tests
│ ├── ProductionCode.c
│ ├── ProductionCode.h
│ ├── TestProductionCode.c
│ └── TestProductionCode_Runner.c
└── Unity└── src├── meson.build├── unity.c├── unity.h└── unity_internals.h
4、运行测试程序
在 build 目录下输入 cmake ..命令,会自动生成 Makefile 文件,然后输入make,就会自动编译生成可执行文件 TDD_test 在 TDDUnityExample /bin 下。
进入 TDDUnityExample /bin 下,输入./TDD_test。
输出结果为:
test/TestProductionCode.c:0:test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode:PASS
test/TestProductionCode.c:33:test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken:FAIL: Expected 1 Was 0
test/TestProductionCode.c:0:test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue:PASS
test/TestProductionCode.c:0:test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain:PASS
test/TestProductionCode.c:61:test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed:FAIL: Expected 0x00001234 Was 0x00005A5A-----------------------
5 Tests 2 Failures 0 Ignored
FAIL
从结果来看第二个和第五个测试例程出现错误,错误原因也标记出来了。
5、常用断言
TEST_PASS(); /* 中止测试的其余部分, 但将测试计为通过 */
TEST_PASS_MESSAGE("message")TEST_IGNORE(); /* 将测试用例标记为忽略, 但将测试计为通过 */
TEST_PASS_MESSAGE("message")TEST_FAIL(); /* 中止测试的其余部分, 但将测试计为失败 */
TEST_FAIL_MESSAGE("message")TEST_MESSAGE(""); /* 将消息输出 */
以上断言宏都是放在编写的测试函数中,TEST_IGNORE 一般放在函数的顶部,用来表示将测试用例忽略,其他宏可以放在函数的任意位置。
总结:
unity单元测试框架核心是一个 C 文件和一对头文件,特点是简洁易用、轻量级、可移植性、支持测试断言等。其中有许多测试断言需要多了解多使用。
好了以上就是Unity单元测试框架的简易使用方法,有什么疑问和建议欢迎在评论区中提出,想要了解更多的Unity知识可以去官网上查看,官网上也有详细的教程和实例。
相关文章:
一个简单好用的C语言单元测试框架-Unity
Unity简介: Unity是一个用于C语言的轻量级单元测试框架。它由Throw The Switch团队开发,旨在简化嵌入式系统的单元测试。单元测试中单元的含义,单元就是人为规定的最小的被测功能模块,如C语言中单元指一个函数,Java里…...
ubuntu系统 vscode 配置c/c++调试环境
文章目录 1.安装插件2.目录结构3.cmake tools配置 1.安装插件 c/c插件 cmake cmake tools插件 2.目录结构 . ├── build ├── CMakeLists.txt ├── demo │ └── main.cpp ├── image.png ├── src │ ├── add.cpp │ └── add.hpp └── vsdebug.…...
算法练习-A+B/财务管理/实现四舍五入/牛牛的菱形字符(题目链接+题解打卡)
难度参考 难度:简单 分类:熟悉OJ与IDE的操作 难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。以下内容均为个人笔记,旨在督促自己认真学习。 题目 A B1. A B - AcWing题库财务管理1004:财…...
XSS语句
XSS测试语句 在测试网站是否存在XSS漏洞时,应该输入一些标签如<,>输入后查看网页源代码是否过滤标签,如果没过滤,很大可能存在XSS漏洞。 <h5>1</h5> <span>1</span> <SCRIPT>alert(document.cookie)&l…...
AD导出BOM表 导出PDF
1.Simple BOM: 这种模式下,最好在pcb界面,这样的导出的文件名字是工程名字,要是在原理图界面导出,会以原理图的名字命名表格。 直接在菜单栏 报告->Simple BOM 即可导出物料清单,默认导出 comment pattern qu…...
linux 的nobody是什么用户? 对安全有没有影响?
目 录 一、前言:nobody是不是可疑用户? 二、Linux系统中的nobody用户? 二、有nobody用户存在,安全吗? 一、前言:nobody是不是可疑用户? 在前面一篇文章“Linux安全问题,如何查看哪…...
2024年华数杯国际数学建模B 光伏电(Problem B: Photovoltaic Power)完整思路以及源代码分享
背景 中国的电力构成包括传统的能源发电(如煤炭、石油和天然气)、可再生能源发电 (如水力发电、风能、太阳能和核能)和其他形式的电力。这些发电方式在满足中 国巨大的电力需求方面发挥着至关重要的作用。根据最新数据…...
在 Spring MVC 中,用于接收前端传递的参数的注解有以下几种
目录 RequestParam: PathVariable: RequestBody: RequestHeader: CookieValue: RequestParam: 用于获取请求参数的值。可以指定参数名称和默认值。示例代码: GetMapping("/users&q…...
K8s常用命令
查看集群各节点的状态 部署应用 删除一个service服务 查询service服务列表 kubectl get services 查看网络资源 kubectl get svc pod 创建一个namespace kubectl create namaspace namespace名称 创建一个pod 通常不需要创建pod 查看pod kubectl get pods kube…...
MySQL的基本操作
目录 序言 一、SQL语句(Structured Query Language) 1.1 SQL简介 1.2 SQL语句的分类 1.3 SQL语句的书写规范 二、数据库操作 2.1 查看库 2.2 创建库 2.3 切换库 2.4 删除库 三、MySQL字符集 3.1 MySQL字符集的分类 3.2 UTF8和UTF8MB4的区别…...
【b站咸虾米】chapter4_vue组件_新课uniapp零基础入门到项目打包(微信小程序/H5/vue/安卓apk)全掌握
课程地址:【新课uniapp零基础入门到项目打包(微信小程序/H5/vue/安卓apk)全掌握】 https://www.bilibili.com/video/BV1mT411K7nW/?p12&share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 四、vue组件 uni-app官网 …...
Java网络编程——UDP通信原理
一、TCP和UDP概述 传输层通常以TCP和UDP协议来控制端点与端点的通信 TCPUDP协议名称传输控制协议用户数据包协议是否连接面向连接的协议。数据必须要建立连接无连接的协议,每个数据报中都给出完整的地址信息,因此不需要事先建立发送方和接受方的连接是…...
Spring | Srping AOP (AOP简介、动态代理、基于“代理类”的AOP实现)
目录: 1.Spring AOP简介1.1 AOP简介1.2 AOP术语 2.动态代理2.1 JDK动态代理2.2 CGLIB代理 3.基于“代理类”的AOP实现3.1 Spring的通知类型3.2 ProxyFactoryBean ( 可通知.xml配置文件完成aop功能 ) 1.Spring AOP简介 1.1 AOP简介 Spring的AOP模块,是Spring框架体系…...
StarRocks 生成列:百倍提速半结构化数据分析
半结构化分析主要是指对 MAP,STRUCT,JSON,ARRAY 等复杂数据类型的查询分析。这些数据类型表达能力强,因此被广泛应用到 OLAP 分析的各种场景中,但由于其实现的复杂性,对这些复杂类型分析将会比一般简单类型…...
数据结构---数组
一、基本概念 1. 存放一组相同数据类型的集合 2.在内存中,分配连续的空间,数组创建时要指定大小 3. 定义 数据类型 [] 数组名 // 1.定义一个数组,里面的元素包含10, 20, 24, 17, 35, 58, 45, 74 int arr[] {10, 20, 24, 17, 35, 58, 45, 74}; 4. 获取数组的长度 int lengt…...
知识笔记(八十四)———链式语句中fetchSql和force和bind用法
fetchSql: fetchSql用于直接返回SQL而不是执行查询,适用于任何的CURD操作方法。 例如: $result Db::table(think_user)->fetchSql(true)->find(1);输出result结果为: SELECT * FROM think_user where id 1 force&#…...
为什么要用B+树
B树的优势 支持范围查询:B树在进行范围查询时,只需要从根节点一直遍历到叶子节点,因为数据都存储在叶子节点上,而且叶子节点之间有指针连接,可以很方便的进行范围查询 支持排序:B树的叶子节点按照关键字顺…...
Android 通过adb命令查看应用流量
一. 获取应用pid号 通过adb shell ps -A | grep 包名 来获取app的 pid号 二. 查看应用流量情况 使用adb shell cat /proc/#pid#/net/dev 命令 来获取流量数据 备注: Recevice: 表示收包 Transmit: 表示发包 bytes: 表示收发的字节数 packets: 表示收发正确的…...
超全的测试类型详解,再也不怕面试答不出来了!
在软件测试工作过程中或者在面试过程中经常会被问到一些看起来简单但是总是有些回答不上的问题,比如你说说“黑盒测试和白盒测试的区别?”,“你们公司做灰度测试么?", ”α测试和β测试有什么不一样?“࿰…...
【Linux】
Linux零基础入门 列出文件/文件夹新建/切换路径查看当前路径重命名或者移动文件夹拷贝文件/文件夹删除文件夹设置环境变量编辑文本文件压缩和解压查看cpu的信息查看/杀死进程查看进程的CPU和内存占用重定向日志场景一场景二场景三场景四 列出文件/文件夹 命令:Ls(L…...
【全网最全图文版】Windows 版 Open Claw v 2.7.5 纯净版搭建教程
📌 前言 开源圈热门的「数字员工」OpenClaw(昵称小龙虾),GitHub 星标突破 28 万,凭借本地运行 零代码操作 自动干活的核心优势广受关注!很多人误以为它是普通聊天 AI,实则是能真正操控电脑的…...
食品制造 | 品控AI自动化方案主流厂商横评:2026企业级智能体选型与落地实测
2026年,全球食品制造业正处于从“数字化转型”向“智能化深耕”跨越的关键节点。随着国家市场监管总局“互联网AI监管”战略的全面深化,食品安全已不再仅仅依赖于周期性的线下抽检,而是转向了基于AI技术的全时段、全链路实时监控。 从校园食堂…...
ESP-01/01S 固件烧录实战:从零到一,解锁Wi-Fi模块核心功能
1. 认识ESP-01/01S:你的第一个Wi-Fi模块 第一次拿到ESP-01或ESP-01S这个小家伙时,你可能会觉得它像个黑色的小饼干——尺寸只有24.8mm x 14.3mm,却集成了完整的Wi-Fi功能。我在2016年第一次接触这个模块时,就被它的性价比震惊了&a…...
RT-Thread裁剪实战:从98KB到28KB的嵌入式系统瘦身指南
1. 项目概述:为什么我们需要裁剪RT-Thread?如果你是一名嵌入式软件工程师,或者正在学习RT-Thread,那么“裁剪”这个词对你来说一定不陌生。RT-Thread作为一款优秀的国产开源实时操作系统,其标准版(或称完整…...
【语音检测】基于matlab GUI短时自相关的基音周期检测【含Matlab源码 15451期】
💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞Ὁ…...
为内部知识库问答系统集成taotoken多模型路由提升回答质量
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为内部知识库问答系统集成taotoken多模型路由提升回答质量 构建一个高效、准确的内部知识库问答系统,是许多企业提升信…...
告别手写代码!用Roboflow的Auto-Orient和Mosaic增强你的YOLO数据集(附完整流程)
零代码实现YOLO数据集增强:Roboflow自动化工具全解析 在目标检测领域,数据质量往往直接决定模型性能上限。传统数据增强方法需要开发者手动编写Python脚本调整图像方向、处理标注格式,不仅耗时耗力,还容易因格式兼容性问题导致训练…...
Armbian编译避坑指南:如何绕过‘Docker不可用’及国内网络依赖问题,成功构建RK3588固件
Armbian编译实战:RK3588平台高效构建与网络优化策略 当国内开发者尝试为RK3588这类高性能ARM平台定制Armbian系统时,往往会遇到两个"拦路虎":Docker环境配置报错和海外资源下载困难。本文将以Rock 5B开发板为例,通过全本…...
京东购物自动化评价:3步解放双手的Python智能助手
京东购物自动化评价:3步解放双手的Python智能助手 【免费下载链接】jd_AutoComment 自动评价,仅供交流学习之用 项目地址: https://gitcode.com/gh_mirrors/jd/jd_AutoComment 还在为京东购物后堆积如山的待评价订单烦恼吗?每次大促后面对几十个商…...
WindowResizer:打破Windows窗口尺寸限制的终极方案
WindowResizer:打破Windows窗口尺寸限制的终极方案 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 在Windows日常使用中,你是否曾对某些应用程序的窗口尺寸…...
