《测试驱动的嵌入式C语言开发》——2.2节Unity:一个全部用C实现的自动化测试框架...

2.2 Unity:一个全部用C实现的自动化测试框架
Unity是一个简单且直接的自动化单元测试框架。它由很少的几个文件构成。让我们通过几个示例单元测试用例来认识一下Unity和单元测试。如果你是一个长期的Unity用户,你会发现如果不用Unity所提供的脚本来生成测试运行容器,那么额外的几个宏可能会很有帮助。
用Unity写的sprintf()测试用例
测试要写得短并且有重点。可以把它想象成一个安检过程,通过时保持安静,失败时就要发出警报声。下面这个测试让sprintf()处理一个没有格式操作的格式声明。


e0e4e59586e7f7fb62b6f31fb0a4feaeab99a0e3

宏TEST()用来定义一个测试函数,当运行所有测试的时候就会调用它。第一个参数是一个测试的组名。第二个参数是这个测试的名字。我们将在第3章中更仔细地介绍TEST()这个宏。
宏TEST_ASSERT_EQUAL()用来比较两个整数。sprintf()应该返回给测试说它格式化的结果字符串长度为3,并且如果是这样的,TEST_ASSERT_EQUAL()就成功了。像大多数的自动化测试框架一样,TEST_ASSERT_EQUAL()的第一个参数是期望中的值。
TEST_ASSERT_EQUAL_STRING()用来比较两个以null结尾的字符串。这句话是声明output的内容必须是字符串“hey”。按照习惯,第一个参数是期望值。
如果任意一个检查的条件不满足,测试就会失败。这些检查会按顺序进行,TEST()会在碰到第一个失败时终止。
注意,TEST_ASSERT_EQUAL_STRING()可能在意外的情况下通过。如果output的内容刚好本来就是“hey”,那么如果sprintf()什么也不干,这个测试也会通过。当然,这不大可能发生,但我们最好还是改进一下这个测试,把output初始化成一个空字符串。


237d19ee72585f954c1d15c8f9fa51047b32b555


a90c9ec98c318e3f80e202cd4fb29c38c18c9937

如果我们担心sprintf()会破坏output之前的内存,我们也可以把output的大小再加一个字符,然后把&output[1]传给sprintf()。最后检查一下output[0]是否还是0xaa,这在很大程度上能证明sprintf()的行为是正确的。
在C语言中,要让测试面面俱到很难。错误的或是恶意的代码可以在output之后或之前距离很远的地方搞破坏。把测试做到什么程度是需要我们自己来判断的。后面你将会看到在TDD中如何决定要写哪些测试。
在以上的测试中,你可以看到其中正在滋生一些细微的重复。output的声明是重复的,初始化是重复的,以及对写过界的检查也是重复的。如果只有两个测试,可能问题不大,但如果你刚好是维护sprintf()的人,你需要比这多得多的测试。随着测试的增加,这些重复会越来越多并且让测试用例中的代码难以理解。接下来让我们看一看测试夹具是如何帮我们避免TEST()测试用例中的重复的。
Unity中的测试夹具
使用测试夹具的目的是避免重复。测试夹具把所有测试都需要的那些部分组织在一处。请留意以下对sprintf()测试的代码中是如何使用TEST_SETUP()和TEST_TEAR_DOWN()来避免重复的。


8bbd52bba36aeb0ca3228668ab32e767a09ac5d1


6f6f2c05fd09db39fd8e5595c3710cdd7426d1ea

在TEST_GROUP()之后定义的共享数据会在TEST_SETUP() 中进行初始化,TEST_SETUP() 将在每个TEST()的起始大括号之前执行。这些数据在源文件内可见,而且可以被源文件内的TEST() 和辅助函数使用。在我们的这个TEST_GROUP()组中不需要在TEST_TEAR_DOWN()中做任何清理工作。
作用域为源文件的辅助函数expect()和 given()会帮助sprinitf()的测试保持整洁和不重复。
说到底,这就是普通的C代码,所以你可以任意使用共享数据和辅助函数。我给出的是一种典型的结构,用于有共享数据和检查条件的一组测试。
现在这些测试更专注、简约、直接、能说明问题。

你会发现,一旦你理解了一个特定的TEST_GROUP()并且看过了几个例子以后,要想写出下一个测试用例会简单很多。当一个TEST_GROUP() 中有一个共同的模式时,每个测试都变得很容易阅读、理解和拓展。
安装Unity测试
从前面的例子中很难看出测试用例是如何运行并使用必要的前置和后置过程的,而这是通过另一个叫TEST_GROUP_RUNNER()的宏来完成的。TEST_GROUP_RUNNER()可以和测试放在一起,也可以放在不同的文件里。为了避免总是要在同一文件里上下滚动,本书使用了不同的文件。对于sprintf()的两个测试,TEST_GROUP_RUNNER()看起来如下所示:


77772f6ad085ad91dc419bc12ddd43d4824e7091

每个测试用例都通过RUN_TEST_CASE()宏来调用。基本上就相当于RUN_GROUP_RUNNER()会依次调用以下这些函数体:


2c7a6051da737b44fc1fc2fb274273783bb1698c


e898a6593886e7abdbab6c1986b5ec87460e186e

RUN_TEST_GROUP(测试组名)会调用由TEST_GROUP_RUNNER()定义的函数。所以每个你希望main()来运行的TEST_GROUP_RUNNER()都要加上一个RUN_TEST_GROUP。请注意,一定要把RunAllTests()传给UnityMain()函数。
使用纯C实现的测试框架比较遗憾的一点是,你要记得把每个TEST()加到TEST_GROUP_RUNNER()中,并且整个容器将被UnityMain()函数调用。如果你忘了,测试会被编译但不会执行,可能出错了你也不会知道。
因为存在这种可能出错的情况,所以Unity设计者创建了一个代码生成系统,可以读取你的测试文件并生成测试容器代码。为了让刚开始使用时减少对Unity的依赖,我将不会使用代码生成脚本,而是手工把测试代码串联起来。
在下一节我们介绍CppUTest时,你会看到这个问题的另一种解决办法。在这之前先让我们来看看Unity的输出。
Unity的输出
测试的运行应该作为测试自动构建的一部分。应该用一个简单的命令就可以构建并运行构建出来的测试可执行程序。你会发现我经常构建,每次小的改动后都会重新构建。这就是TDD。我把我的开发环境设置成每次我保存一个文件时就会自动构建所有的东西。测试的输出就像这样:


d7e5b932d4dac4768963ac111628ad1694afa5be

请注意,在所有测试都通过时,输出最少。随便瞟一眼,如果看到一行写着“OK”的文字,这意味着“所有测试通过”。这符合UNIX风格,测试框架遵循“没有消息就是最好的消息”原则(稍后你会看到,当有测试失败时它会给出特定的错误信息)。它看上去很直白,但还是让我们来看看每行输出都是什么含义。
请注意,每个测试用例运行时都会打印出一个点(.)。如果测试运行起来时间很长,这会让我们感知到有东西在运行。分隔符(---)那一行只是用来分隔测试的总结。
Tests—总共的TEST()个数。
Failures—失败的TEST()的个数。
Ignored—处在忽略状态的测试的个数。忽略的测试会被编译但不会执行。
让我们加一个出错的测试来看看会发生什么。看看测试的输出,我们故意加的错误应该很明显:


e4fa2052f0d0c747a02d54be63cfe6e45a20891a

失败信息会报告失败的测试用例所在的文件名和行号、测试用例的名字,以及为什么会失败。并且请留意最后的总结中显示一个测试失败了。
你可以在附录B中找到更多关于Unity的信息。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值