μCUnit,微控制器的单元测试框架

  在MCU on Eclipse网站上看到Erich Styger在8月26日发布的博文,一篇关于微控制器单元测试的文章,有很高的参考价值,特将其翻译过来以备学习。原文网址:https://mcuoneclipse.com/2018/08/26/tutorial-%CE%BCcunit-a-unit-test-framework-for-microcontrollers/

  单元测试是主机开发的常见做法。但对于嵌入式开发,这似乎仍然是一个“空白”领域。主要是因为嵌入式工程师不习惯单元测试,或者因为单元测试的通常框架需要嵌入式目标上的太多资源?

  我使用的是μCUnit框架,它是一个小巧易用的框架,面向小型微控制器应用。

uCUnit

  框架非常简单:两个头文件和一个.c文件:

uCUnit框架文件

  使用uCUnit GitHub站点中的原始站点或使用我从GitHub稍微调整和修改的站点,以与MCUXpresso SDK和IDE一起使用

  概念是单元测试包括提供测试宏的uCunit.h头文件。

  头文件中的#define将输出配置为详细或正常:

UCUNIT_MODE_NORMAL或UCUNIT_MODE_VERBOSE

  System.c和System.h是系统的连接,主要用于启动,关闭和打印测试结果到控制台。下面是使用printf()方法写入输出的实现,但是这可以被任何写入例程替换或扩展到SD卡上的日志文本。

 1 /* Stub: Transmit a string to the host/debugger/simulator */
 2 void System_WriteString(char * msg) {
 3
 4     PRINTF(msg);
 5
 6 }
 7
 8 void System_WriteInt(int n) {
 9
10     PRINTF("%d", n);
11
12 }

框架概述

  首先,我必须包含单元测试框架头文件:

#include "uCUnit.h"

  接着,我必须初始化框架

UCUNIT_Init(); /* initialize framework */

  还有一个测试用例包含在UCUNIT_TestcaseBegin()和UCUNIT_TestcaseEnd()中:

UCUNIT_TestcaseBegin("Crazy Scientist");

/* test cases ... */

UCUNIT_TestcaseEnd();

  在最后使用时写一个摘要

UCUNIT_WriteSummary();

  如果系统应该关闭使用a

UCUNIT_Shutdown();

测试

  该框架提供了多种测试方法,例如:

UCUNIT_CheckIsEqual(x, 0); /* check if x == 0 */

UCUNIT_CheckIsInRange(x, 0, 10); /* check 0 <= x <= 10 */

UCUNIT_CheckIsBitSet(x, 7); /* check if bit 7 set */

UCUNIT_CheckIsBitClear(x, 7); /* check if bit 7 cleared */

UCUNIT_CheckIs8Bit(x); /* check if not larger then 8 bit */

UCUNIT_CheckIs16Bit(x); /* check if not larger then 16 bit */

UCUNIT_CheckIs32Bit(x); /* check if not larger then 32 bit */

UCUNIT_CheckIsNull(p); /* check if p == NULL */

UCUNIT_CheckIsNotNull(s); /* check if p != NULL */

UCUNIT_Check((*s)==’\0’, "Missing termination", "s"); /* generic check: condition, msg, args */

  通过几个例子可以解释这一点。

示例:疯狂的科学家

  下面是一个‘crazyScientist‘功能,它结合了不同的材料:

 1 typedef enum {
 2     Unknown,  /* first, generic item */
 3     Hydrogen, /* H */
 4     Helium,   /* He */
 5     Oxygen,   /* O */
 6     Oxygen2,  /* O2 */
 7     Water,    /* H2O */
 8     ChemLast  /* last, sentinel */
 9 } Chem_t;
10
11 Chem_t crazyScientist(Chem_t a, Chem_t b) {
12     if (a==Oxygen && b==Oxygen) {
13         return Oxygen2;
14     }
15
16     if (a==Hydrogen && b==Oxygen2) {
17         return Water;
18     }
19
20     return Unknown;
21
22 }

  对此的测试可能如下所示:

 1 void Test(void) {
 2   Chem_t res;
 3   UCUNIT_Init(); /* initialize framework */
 4
 5   UCUNIT_TestcaseBegin("Crazy Scientist");
 6   res = crazyScientist(Oxygen, Oxygen);
 7   UCUNIT_CheckIsEqual(res, Oxygen2);
 8   UCUNIT_CheckIsEqual(Unknown, crazyScientist(Water, Helium));
 9   UCUNIT_CheckIsEqual(Water, crazyScientist(Hydrogen, Oxygen2));
10   UCUNIT_CheckIsEqual(Water, crazyScientist(Oxygen2, Hydrogen));
11   UCUNIT_CheckIsInRange(crazyScientist(Unknown, Unknown), Unknown, ChemLast);
12   UCUNIT_TestcaseEnd();
13
14   /* finish all the tests */
15   UCUNIT_WriteSummary();
16   UCUNIT_Shutdown();
17 }

  通过不同的检查,我们可以验证功能是否正在按照我们的预期进行。它产生以下输出:

======================================

Crazy Scientist

======================================

../source/Application.c:60: passed:IsEqual(res,Oxygen2)

../source/Application.c:61: passed:IsEqual(Unknown,crazyScientist(Water, Helium))

../source/Application.c:62: passed:IsEqual(Water,crazyScientist(Hydrogen, Oxygen2))

../source/Application.c:63: failed:IsEqual(Water,crazyScientist(Oxygen2, Hydrogen))

../source/Application.c:64: passed:IsInRange(crazyScientist(Unknown, Unknown),Unknown,ChemLast)

======================================

../source/Application.c:65: failed:EndTestcase()

======================================

**************************************

Testcases: failed: 1

passed: 0

Checks:    failed: 1

passed: 4

**************************************

System shutdown.

  我建议在执行之前编写单元测试*,因为这样我就可以考虑所有不同的极端情况并改进要求。

  以上输出设置为UCUNIT_MODE_VERBOSE。使用UCUNIT_MODE_NORMAL,它使用更紧凑的格式并仅打印失败的测试:

======================================

Crazy Scientist

======================================

../source/Application.c:63: failed:IsEqual(Water,crazyScientist(Oxygen2, Hydrogen))

======================================

../source/Application.c:65: failed:EndTestcase()

======================================

**************************************

Testcases: failed: 1

passed: 0

Checks:    failed: 1

passed: 4

**************************************

System shutdown.

跟踪点

  在上面的例子中,我们只是从外部测试函数的功能。如何检查以下函数中的测试确实检查除以零的情况?

1 int checkedDivide(int a, int b) {
2     if (b==0) {
3         PRINTF("division by zero is not defined!\n");
4         return 0;
5     }
6     return a/b;
7 }

  要检查是否真的输入了if()条件,我可以添加一个跟踪点。跟踪点的数量在μCUnit.h中配置为:

/**

 * Max. number of checkpoints. This may depend on your application

 * or limited by your RAM.

 */

#define UCUNIT_MAX_TRACEPOINTS 16

  和

UCUNIT_ResetTracepointCoverage();

  我可以重置跟踪点。

  我用跟踪标记执行跟踪点(在0..UCUNIT_MAX_TRACEPOINTS-1范围内)

UCUNIT_Tracepoint(id);

  和

UCUNIT_CheckTracepointCoverage(0);

  我可以检查是否触摸了给定的跟踪点。在要测试的功能下面有一个跟踪点:

1 int checkedDivide(int a, int b) {
2     if (b==0) {
3         UCUNIT_Tracepoint(0); /* mark trace point */
4         PRINTF("division by zero is not defined!\n");
5         return 0;
6     }
7     return a/b;
8 }

  相应的单元测试代码:

1 UCUNIT_TestcaseBegin("Checked Divide");
2 UCUNIT_CheckIsEqual(100/5, checkedDivide(100,5));
3 UCUNIT_ResetTracepointCoverage(); /* start tracking */
4 UCUNIT_CheckIsEqual(0, checkedDivide(1024,0));
5 UCUNIT_CheckTracepointCoverage(0); /* check coverage of point 0 */
6 UCUNIT_TestcaseEnd();

  然后生成:

======================================

Checked Divide

======================================

../source/Application.c:69: passed:IsEqual(100/5,checkedDivide(100,5))

division by zero is not defined!

../source/Application.c:71: passed:IsEqual(0,checkedDivide(1024,0))

../source/Application.c:72: passed:TracepointCoverage(1)

字符串测试

  还有许多其他方法可以使用检查,最多可以使用用户配置的检查和消息。以下是要测试的函数的示例:

1 char *endOfString(char *str) {
2   if (str==NULL) {
3     return NULL;
4   }
5   while(*str!=‘\0‘) {
6     str++;
7   }
8   return str;
9 }

  使用以下测试代码:

 1 UCUNIT_TestcaseBegin("Strings");
 2 UCUNIT_CheckIsNull(endOfString(NULL));
 3 str = endOfString("abc");
 4 UCUNIT_Check(
 5     (str!=NULL), /* condition to check */
 6     "string shall be not NULL", /* message */
 7     "str" /* argument as string */
 8     );
 9 UCUNIT_CheckIsEqual(‘\0‘, *endOfString(""));
10 UCUNIT_CheckIsEqual(‘\0‘, *endOfString("hello"));
11 str = endOfString("world");
12 UCUNIT_CheckIsNotNull(str);
13 UCUNIT_CheckIsEqual(‘\0‘, *str);
14 UCUNIT_TestcaseEnd();

  其输出:

======================================

Strings

======================================

../source/Application.c:76: passed:IsNull(endOfString(NULL))

../source/Application.c:82: passed:string shall be not NULL(str)

../source/Application.c:83: passed:IsEqual(‘\0‘,*endOfString(""))

../source/Application.c:84: passed:IsEqual(‘\0‘,*endOfString("hello"))

../source/Application.c:86: passed:IsNotNull(str)

../source/Application.c:87: passed:IsEqual(‘\0‘,*str)

概要

  μCUnit是一个非常简单但功能强大的嵌入式设备和微控制器单元测试框架。它易于使用,只需要极少的资源,并通过自动化单元测试帮助提高嵌入式软件的质量。我希望你也觉得它很有用。

链接

欢迎关注:

原文地址:https://www.cnblogs.com/foxclever/p/9573026.html

时间: 2024-10-02 07:46:39

μCUnit,微控制器的单元测试框架的相关文章

Java单元测试框架 JUnit

Java单元测试框架 JUnit JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于KentBeck的sUnit的xUnit家族中为最成功的一个. JUnit有它自己的JUnit扩展生态圈.多数Java的开发环境都已经集成了JUnit作为单元测试的工具. 在线Javadoc:http://ww...更多JUnit信息 最近更新: JUnit 4.12 发布,Java 单元测试框架 发布于4个月前 C++模拟测试框架 Google Mock

Google C++单元测试框架

一.概述 Google C++单元测试框架(简称Gtest),可在多个平台上使用(包括Linux, Mac OS X, Windows, Cygwin和Symbian),它提供了丰富的断言.致命和非致命失败判断,能进行值参数化测试.类型参数化测试."死亡测试".Gtest是一个开源的项目,其源码可以从这里下载,目前的代码发行版是1.6.0. 编译 源码包中的README文件说明了如何编译Gtest源码,目录msvc.xcode中分别包含了Windows.Mac OS X平台相关的项目文

玩转Google开源C++单元测试框架Google Test系列(gtest)之一 初识gtest

进入文件夹执行: ./configure make make install 完毕即可正常使用: (1)包含include目录 -I/root/scp/gtest/gtest-1.3.0: (2)包含lib中的动态链接库:-lgtest -L/root/scp/gtest/gtest-1.3.0/lib 示例代码: [cpp] view plaincopy #include <gtest/gtest.h> int Foo(int a, int b) { if (a == 0 || b == 0

Google C++单元测试框架---GTest的Sample1和编写单元测试的步骤

如果你还没有搭建gtest框架,可以参考我之前的博客:http://www.cnblogs.com/jycboy/p/6001153.html.. 1.The first sample: sample1 你把github上的项目导来之后,github地址:https://github.com/google/googletest,在目录:..(你的目录)\googletest-master\googletest\samples是你的samples文件夹. 在VS中创建项目:GtestSamples

Python自动单元测试框架(摘要笔记)

规范Python单元测试 原文:https://www.ibm.com/developerworks/cn/linux/l-pyunit/ 测试是一个贯穿于整个开发过程的连续过程,从某个意义上说,软件开发的过程实际上就是测试过程.正如Martin Fowler所说的"在你不知道如何测试代码之前,就不该编写程序.而一旦你完成了程序,测试代码也应该完成.除非测试成功,你不能认为你编写出了可以工作的程序." 测试最基本的原理就是比较预期结果是否与实际执行结果相同,如果相同则测试成功,否则测试

Selenium基于Python web自动化基础二 -- 免登录、等待及unittest单元测试框架

一.免登录在进行测试的过程中难免会遇到登录的情况,给测试工作添加了工作量,本文仅提供一些思路供参考解决方式:手动请求中添加cookies.火狐的profile文件记录信息实现.人工介入.万能验证码.去掉验证码 1.手动在请求中添加cookies信息 1 url = "http://www.baidu.com" 2 driver = webdriver.Firefox() 3 driver.get(url) 4 time.sleep(3) 5 #添加cookies的方式 6 7 c1 =

Python单元测试框架之pytest---如何执行测试用例

介绍 pytest是一个成熟的全功能的Python测试工具,可以帮助你写出更好的程序. 适合从简单的单元到复杂的功能测试 l 模块化parametrizeable装置(在2.3,持续改进) l 参数化测试函数(用例) l 标记测试功能与属性 l Skip和xfail:处理不成功的测试用例(在2.4改进) l 通过xdist插件分发测试到多个CPU l 不断地重新运行失败的测试 l 灵活约定的Python测试发现 Home Page: http://pytest.org 安装 >pip insta

如何使用VS2013本地C++单元测试框架

在VS2013中,可以使用VS自带的C++单元测试框架. 在使用该框架前,需要先安装Unit Test Generator(可以通过菜单“工具->扩展和更新”搜索安装). 下边,就阐述一下利用该框架的步骤: 1. 新建空的Win32控制台项目NativeUnitTest 将该工程的"配置类型"改为“动态库(dll)”,如下图: 2. 新建单元测试工程 在解决方案NativeUnitTest下新建单元测试工程,“Visual C++ -> 测试 -> 本机单元测试项目”

Python单元测试框架 unittest

Python单元测试框架 作者: Steve Purcell, <stephen_purcell at yahoo dot com>翻译: Heiz, <heiz dot yuan at gmail dot com>项目网站: http://pyunit.sourceforge.net/ 目录 概况 系统要求 使用PyUnit构建自己的测试 安装 测试用例介绍 创建一个简单测试用例 复用设置代码:创建固件 包含多个测试方法的测试用例类 将测试用例聚合成测试套件 嵌套测试用例 测试代