c语言单元测试框架–CuTest

c语言单元测试框架–CuTest1 简介 CuTest 是一款微小的 C 语言单元测试框 是我迄今为止见到的最简洁的测试框架之一 只有 2 个文件 CuTest c 和 CuTest h 全部代码加起来不到一千行 麻雀虽小 五脏俱全 测试的构建 测试的管理 测试语句 都全部包含在内 2 CuTest 剖析 2 1 断言一个测试 case 是否通过落到代码实处 就是对测试值与期待值之间进行比较 这就要用到断言 defineCu

1、简介

CuTest是一款微小的C语言单元测试框,是我迄今为止见到的最简洁的测试框架之一,只有2个文件,CuTest.c和CuTest.h,全部代码加起来不到一千行。麻雀虽小,五脏俱全,测试的构建、测试的管理、测试语句,都全部包含在内。

2、CuTest剖析

2.1 断言

一个测试case是否通过落到代码实处,就是对测试值与期待值之间进行比较,这就要用到断言。

#define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac)) #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) #define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl)) #define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac))
......
......




以数字测试为例CuAssertIntEquals,其实现为:

void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, int expected, int actual) { char buf[STRING_MAX]; if (expected == actual) return; sprintf(buf, "expected <%d> but was <%d>", expected, actual); CuFail_Line(tc, file, line, message, buf); }

sprintf(buf, "expected <%d> but was <%d>", expected, actual); CuFail_Line(tc, file, line, message, buf);

void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message) { CuString string; CuStringInit(&string); if (message2 != NULL) { CuStringAppend(&string, message2); CuStringAppend(&string, ": "); } CuStringAppend(&string, message); CuFailInternal(tc, file, line, &string); } static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) { char buf[HUGE_STRING_LEN]; sprintf(buf, "%s:%d: ", file, line); CuStringInsert(string, buf, 0); tc->failed = 1; tc->message = string->buffer; if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); }

到这里,一个错误的测试就会从longjmp返回。

2.2 测试的组织

int AddInt(int a, int b);

测试用例:

void test_add(CuTest* tc) { CuAssert(tc, "\r\ntest not pass", 2 == AddInt(1,0); } CuSuite* TestAdd(void) { CuSuite* suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_add); return suite; }

如果有许多测试,则要用到测试组的管理。也就是测试case的管理,CuTest中叫做suite。

CuSuite* CuGetSuite(void) { CuSuite* suite = CuSuiteNew(); SUITE_ADD_TEST(suite, TestCuStringAppendFormat); SUITE_ADD_TEST(suite, TestCuStrCopy); SUITE_ADD_TEST(suite, TestFail); SUITE_ADD_TEST(suite, TestAssertStrEquals); SUITE_ADD_TEST(suite, TestAssertStrEquals_NULL); return suite; }

一般而言suite是一类测试的集合,其实就是调用了CuSuiteAdd函数。

#define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST))

用宏展开,#TEST等价于TEST内容转换为字符串,CuTestNew(#TEST, TEST)是宏的一种妙用。此函数作用是把case加入到testSuite的具体链表中去。

void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) { assert(testSuite->count < MAX_TEST_CASES); testSuite->list[testSuite->count] = testCase; testSuite->count++; }

上面是一类测试,用suite函数SUITE_ADD_TEST来实现多个测试函数的归类管理。那么有多个的函数的测试时候,是如何规划呢,需要suite上再添加suite了。最后对上层接口提供一个总的suite的引用即可。

 CuSuite* suite = CuSuiteNew(); CuSuiteAddSuite(suite, CuGetSuite()); CuSuiteAddSuite(suite, CuStringGetSuite()); CuSuiteAddSuite(suite, TestAdd());

2.3 测试的运行

测试case构成了测试组–suite,然后多个测试组可以合并为一个测试组。测试组的执行就是遍历数组,执行内部的每一个测试case。

void CuSuiteRun(CuSuite* testSuite) { int i; for (i = 0 ; i < testSuite->count ; ++i) { CuTest* testCase = testSuite->list[i]; CuTestRun(testCase); if (testCase->failed) { testSuite->failCount += 1; } } }

测试的执行靠CuTestRun来完成,依旧是打下跳转断点–setjmp(buf),然后运行测试case,如果测试case无错误,则安静的退出,否则记录出错信息,然后longjmp返回到if (setjmp(buf) == 0)一行,在CuSuiteRun中,会对错误case的个数进行计数,以便全部case运行完毕后,输出总结信息用。

void CuTestRun(CuTest* tc) { jmp_buf buf; tc->jumpBuf = &buf; if (setjmp(buf) == 0) { tc->ran = 1; (tc->function)(tc); } tc->jumpBuf = 0; }

上面的函数,测试函数的调用很隐晦,是(tc->function)(tc)语句完成的。测试case的原型为:

typedef void (*TestFunction)(CuTest *); struct CuTest { char* name; TestFunction function; int failed; int ran; const char* message; jmp_buf *jumpBuf; };

CuTest* CuTestNew(const char* name, TestFunction function) { CuTest* tc = CU_ALLOC(CuTest); CuTestInit(tc, name, function); return tc; }

第二步,测试case初始化,将funciton引用指针赋值给CuTest* t->function。所以(tc->function)(tc)语句就相当于直接调用测试case函数本体。

void CuTestInit(CuTest* t, const char* name, TestFunction function) { t->name = CuStrCopy(name); t->failed = 0; t->ran = 0; t->message = NULL; t->function = function; t->jumpBuf = NULL; }

3、CuTest实例

void test_add(CuTest* tc) { CuAssert(tc, "\r\ntest not pass", 2 == 1 + 1); }

2)测试组suite

CuSuite* TestAdd(void) { CuSuite* suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_add); return suite; }

3)测试项目结构组织

void main() { RunAllTests(); getchar(); } void RunAllTests(void) { CuString *output = CuStringNew(); CuSuite* suite = CuSuiteNew(); CuSuiteAddSuite(suite, TestAdd()); CuSuiteRun(suite); CuSuiteSummary(suite, output); CuSuiteDetails(suite, output); printf("%s\n", output->buffer); }

 

转载于:https://www.cnblogs.com/pingwen/p/9216004.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/205884.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午4:57
下一篇 2026年3月19日 下午4:58


相关推荐

  • 各类光纤接口类型的区别与图示

    各类光纤接口类型的区别与图示光纤接头类型主要可以分为以下几种 FC 圆型带螺纹 配线架上用的最多 ST 卡接式圆型 SC 卡接式方型 光纤收发器用的较多 LC 卡接式方形 比 SC 略小 光纤交换机用的较多 MT RJ 方型 一头光纤收发一体 光纤单模和多模的标识 L 表示单模 波长 1310 纳米 LH 表示单模长距 波长 1310 纳米 1550 纳米 SM 表示多模 波长 850 纳米 SX LH 表示可以使用

    2026年3月26日
    1
  • 员工管理系统源码_小公司服务器方案

    员工管理系统源码_小公司服务器方案简介:员工管理系统源码是一款:基于ThinkPHP框架开发的员工管理/员工信息记录系统,有增删改查功能,源码全开源,方便二次开发,搭建和使用都很简单安装教程:环境:Linux+mysql5.6+php7.2安装步骤:1.新建站点–》上传–》解压2.导入数据库文件3.修改/数据库 \Application\Home\Conf\config.php4.后台地址/home/login/index账号密码admin  www.ohbbs.cn5

    2026年2月21日
    3
  • 每天一道算法_4_Hangover

    此系列刚开始两天就被十一假期打断了,去山西玩了几天,今天刚回来,为了弥补一下心里的貌似隐隐作痛的愧疚感,补上一刀。今天的题目是 Hangover,如下: DescriptionHow far can you make a stack of cards overhang a table? If you have one card, you can create a max

    2022年3月10日
    36
  • 七个开源的 SpringBoot 前后端分离项目,Star过千,快去收藏夹吃灰吧!

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 微信公众号:江南一点雨 前后端分离已经在慢慢走进各公司的技术栈,根据松哥了解到的消息,不少公司都已经切换到这个技术栈…

    2021年6月24日
    79
  • test.py是什么文件_exe文件反编译源码工具

    test.py是什么文件_exe文件反编译源码工具"java"]//P6DataSourcep6DSource=newP6DataSource(cpDSource)publicclassP6DataSourceextendsP6BaseimplementsDataSource,Referenceable,Serializable{//source是通过构造传入的数据源c3p0或DruidpublicP6DataSource(DataSourcesource)

    2022年10月5日
    5
  • linux扩容文件系统resize2fs,linux resize2fs命令设置方法

    linux扩容文件系统resize2fs,linux resize2fs命令设置方法相信很多人不太了解 resize2fs 命令的用法 下面由学习啦小编为大家整理了 linuxresize2 命令 希望大家喜欢 linuxresize2 命令 resize2fs 命令被用来增大或者收缩未加载的 ext2 ext3 文件系统的大小 如果文件系统是处于 mount 状态下 那么它只能做到扩容 前提条件是内核支持在线 resize linuxkernel2 6 支持在 mount 状态下扩容但

    2026年3月26日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号