《测试驱动的嵌入式C语言开发》—— Unity基础篇(1)

  • Unity介绍

Unity用于C的单元测试(特别是嵌入式软件)[UNIT TESTING FOR C (ESPECIALLY EMBEDDED SOFTWARE)].

Unity本身使用纯C代码编写的,遵循ANSI标准,同时支持大多数的嵌入式编译器。Unity可为64处理器进行单元测试,同样也可以为8位MCU进行单元测试。Unity旨在帮助您充分利用您的测试套件,它具有丰富的断言,因此您可以找到满足您需求的完美匹配。Unity很容易集成到您的工具链中,从基础开始,然后使用可选脚本或者功能启动。Unity很容易扩展,可以通过共用件新的宏以适合您自己的自定义类型。可通过CMock工具获得完整的模拟支持,编写流程或结果的脚本,是Unity拥有无限可能。

  • Unity如何工作

Unity显然是关于断言(assert)的。Unity所有的断言可到https://github.com/ThrowTheSwitch/Unity/blob/master/docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf下查看,但是更建议直接把https://github.com/ThrowTheSwitch/Unity整个工程下载下来,后面会很依靠这个工程里面的内容,断言在嵌入式系统中我们总希望其结果是true。断言最基本的样式是这样的:

int a = 1;
TEST_ASSERT( a == 1 ); //this one will pass
TEST_ASSERT( a == 2 ); //this one will fail

你可以使用上面的TEST_ASSERT,你几乎可以测试你的C代码可以处理的任何东西......但是当出现问题时,你会看到类似这样的东西:

TestMyModule.c:15:test_One:FAIL

这样运行的结果虽然正确,但是输出的信息并没有什么值得参考的价值。有两种方式可以解决这个问题,蛮力方法(适用于非标准case情况):

TEST_ASSERT_MESSAGE( a == 2 , "a isn't 2, end of the world!");

这样输出的结果将会变为:

TestMyModule.c:15:test_one:FAIL:a isn't 2, end of the world!

然后是优雅的解决方案,使用Unity的众多漂亮断言:

TEST_ASSERT_EQUAL_INT(2, a);
TEST_ASSERT_EQUAL_HEX8(5, a);
TEST_ASSERT_EQUAL_UINT16(0x8000, a);

这样,如果单独运行测试的话,将会输出以下失败信息:

TestMyModule.c:15:test_One:FAIL:Expected 2 was 1
TestMyModule.c:23:test_Two:FAIL:Expected 0x05 was 0x01
TestMyModule.c:31:test_Three:FAIL:Expected 32768 was 1

这样测试输出的信息就很有用了。其中第一个参数是预期值,第二个参数是您正在测试的值。他以一种对您来说最方便的格式清晰的打印出来。实际上,Unity可以处理各种类型,而不仅仅是整数。如:

TEST_ASSERT_EQUAL_FLOAT( 3.45, pi );
TEST_ASSERT_EQUAL_STRING( "Attention, Dr. Surly", greeting );

它甚至可以处理您想要添加的自定义消息的情况,您想要检查数据是否Full,或者即想检查数组是否Full并且输出测试信息。如:

TEST_ASSERT_EQUAL_INT_ARRAY( expArray, actualArray, numElements );
TEST_ASSERT_EQUAL_INT_MESSAGE( 5, val, "Not five? Not alive!" );
TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE( e, a, 20, "Oh snap!" );

在每个测试中会使用一个或多个类似的断言。测试只是一个不带参数的C函数。不返回任何内容。按照惯例。它以"test"或者"spec"开头(我将会使用test作为开头):

void test_FunctionUnderTest_should_ReturnFive(void) {
    TEST_ASSERT_EQUAL_INT( 5, FunctionUnderTest() );
    TEST_ASSERT_EQUAL_INT( 5, FunctionUnderTest() ); //twice even!
}

单个测试文件通常会有多个测试。通常,一个测试文件用于测试相应C源文件的所有方面。这可以通过简单的命名约定最清楚:在哪里可以找到MadScience.c的测试?当然,在TestMadScience.c中!

  • 使用Unity进行单元测试

先给出我学习该部分内容的源码https://github.com/LOVEELEC/xUnity/tree/master/Unity该链接对应的是所有Unity学习过程中所用到的全部内容(随着学习的不断进行会不断添加新的内容,同时也有可能会调整原有内容,调整的内容我会尽量在文档中修改同步内容)。该部分使用的的是DumbExample对应的代码。

关于Unity测试工程结构可以参考https://www.udemy.com/unit-testing-and-other-embedded-software-catalysts/中的The Flow对应的视频,有兴趣的小伙伴也可以购买该课程进行学习。

 该部分程序我采用的是最简单的结构,每个proj包含src、test及build文件,同时把对应的Makefile文件也放到同一目录方便编译。其中src包含的被测代码、test中包含的是测试代码、build中包含的编译后的输出内容。具体Makefile中包含的内容我就不献丑了直接给出官方的说明链接http://www.throwtheswitch.org/build/make

假设我们要测试一个名为DumbExample.c的C文件。它看起来像这样:

#include“DumbExample.h” 

int8_t AverageThreeBytes(int8_t a,int8_t b,int8_t c)
{ 
    return(int8_t)(((int16_t)a +(int16_t)b +(int16_t)c)/ 3); 
}

它有一个头文件,如下所示:

#include <stdint.h> 

int8_t AverageThreeBytes(int8_t a,int8_t b,int8_t c);

然后我们制作一个测试文件TestDumbExample.c,它检查一些基本的东西,如翻转和诸如此类的东西:

#include "unity.h"
#include "DumbExample.h"

void test_AverageThreeBytes_should_AverageMidRangeValues(void)
{
    TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(30, 40, 50));
    TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(10, 70, 40));
    TEST_ASSERT_EQUAL_HEX8(33, AverageThreeBytes(33, 33, 33));
}

void test_AverageThreeBytes_should_AverageHighValues(void)
{
    TEST_ASSERT_EQUAL_HEX8(80, AverageThreeBytes(70, 80, 90));
    TEST_ASSERT_EQUAL_HEX8(127, AverageThreeBytes(127, 127, 127));
    TEST_ASSERT_EQUAL_HEX8(84, AverageThreeBytes(0, 126, 126));
}

int main(void)
{
    UNITY_BEGIN();
    RUN_TEST(test_AverageThreeBytes_should_AverageMidRangeValues);
    RUN_TEST(test_AverageThreeBytes_should_AverageHighValues);
    return UNITY_END();
}

所以我们有一个包含两个测试的测试文件。每个测试都有多个断言。如果这些断言中的任何一个失败,那个特定的测试应该失败,我们应该继续进行下一个测试。完成后,它应该输出我们的结果。

            接下来让我们将目录切换到~/TDD/xUnity/Unity/DumbExample:

loveelec@ubuntu:~$ cd ~/TDD/xUnity/Unity/DumbExample/
loveelec@ubuntu:~/TDD/xUnity/Unity/DumbExample$ ls
build  Makefile  src  test
loveelec@ubuntu:~/TDD/xUnity/Unity/DumbExample$ make
-----------------------
IGNORES:
-----------------------

-----------------------
FAILURES:
-----------------------

-----------------------
PASSED:
-----------------------
test/TestDumbExample.c:21:test_AverageThreeBytes_should_AverageMidRangeValues:PASS
test/TestDumbExample.c:22:test_AverageThreeBytes_should_AverageHighValues:PASS

DONE

接下来让我们看看运行效果:

loveelec@ubuntu:~/TDD/xUnity/Unity/DumbExample$ cd build/
loveelec@ubuntu:~/TDD/xUnity/Unity/DumbExample/build$ ls
depends  objs  results  TestDumbExample.out
loveelec@ubuntu:~/TDD/xUnity/Unity/DumbExample/build$ ./TestDumbExample.out 
test/TestDumbExample.c:21:test_AverageThreeBytes_should_AverageMidRangeValues:PASS
test/TestDumbExample.c:22:test_AverageThreeBytes_should_AverageHighValues:PASS

-----------------------
2 Tests 0 Failures 0 Ignored 
OK
loveelec@ubuntu:~/TDD/xUnity/Unity/DumbExample/build$ 

该部分代码只是为了熟悉Unity的功能,并没有按照TDD开发流程进行(先写测试代码再写被测代码)。

  • 代码解析

代码从main入口, UNITY_BEGIN()及UNITY_END()标识测试代码的开始与结束,每个测试均需要用RUN_TEST进行安装,否则将会存在所写的测试代码只会被编译却不会运行测试的问题。该章节完全取自官网内容。详情请参见http://www.throwtheswitch.org/unity

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
测试驱动嵌入C语言开发》是一本探讨嵌入C语言开发测试驱动开发(TDD)方法的书籍。TDD是一种先写测试用例,再编写代码实现这些测试用例的开发,其主要目的是保证代码的质量和可维护性。 这本书主要通过实例演示如何实现嵌入C语言项目的测试驱动开发过程。它介绍了如何使用Unity测试框架和CMock模拟框架来进行单元测试,使得嵌入软件开发过程中的测试更加简单且高效。书中还详细讨论了如何使用不同的测试策略和设计模来提高代码的可测性和可维护性。 此外,书中还介绍了如何使用版本控制系统和持续集成工具来增强测试驱动嵌入C语言开发过程。通过使用这些工具,开发人员可以更好地管理代码库的版本,并及时发现和修复潜在的问题。 《测试驱动嵌入C语言开发》适合具有一定嵌入开发基础的读者,特别是那些希望学习如何应用测试驱动开发方法来提高嵌入软件质量的开发人员。这本书内容丰富,实例详细,可以帮助读者更好地理解和掌握测试驱动开发方法在嵌入C语言开发中的应用。 总之,这本书通过具体实例和详细讲解,向读者展示了如何使用测试驱动开发方法进行嵌入C语言开发,并提高软件质量和可维护性。阅读本书可以使开发人员更好地理解和掌握测试驱动开发的技巧和方法,从而在嵌入开发中取得更好的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值