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

简介: 本节书摘来自华章社区《测试驱动的嵌入式C语言开发》一书中的第2章,第2.2节Unity:一个全部用C实现的自动化测试框架,作者:(美)James W. Grenning,更多章节内容可以访问云栖社区“华章社区”公众号查看

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的信息。

相关文章
|
21天前
|
数据可视化 测试技术 BI
如何设计BI产品的自动化框架
如何设计BI产品的自动化框架
|
21天前
|
数据可视化 jenkins 测试技术
可视化BI类产品如何设计测试框架?
可视化BI类产品如何设计测试框架?
|
21天前
|
Java jenkins 测试技术
常见自动化测试框架和工具
常见自动化测试框架和工具
|
21天前
|
JSON 测试技术 API
一个数据驱动的API测试框架
一个数据驱动的API测试框架
|
21天前
|
存储 测试技术 数据库
数据驱动测试中的参数化
数据驱动测试中的参数化
|
21天前
|
存储 XML 测试技术
什么是数据驱动测试?
什么是数据驱动测试?
|
22天前
|
数据挖掘 测试技术 API
数据驱动API测试
数据驱动API测试
|
24天前
|
测试技术 UED Python
使用Python构建自动化测试框架
本文介绍了如何使用Python语言构建一个强大的自动化测试框架,该框架可以帮助开发人员在软件开发过程中进行高效的测试工作。通过使用Python的强大功能和第三方库,我们可以轻松地编写、运行和管理各种测试用例,并生成详细的测试报告,提高软件质量和开发效率。
|
1月前
|
测试技术 开发者 Python
Python自动化测试与单元测试框架:提升代码质量与效率
在软件开发过程中,测试是不可或缺的环节。Python作为一门广泛应用的编程语言,拥有丰富的自动化测试和单元测试框架,例如unittest和pytest。本文将介绍Python自动化测试的重要性,并深入探讨这两个主流的单元测试框架的特点、使用方法以及优势。通过学习和应用这些框架,开发者可以提高代码质量、提升开发效率,并确保软件在不断迭代中保持稳定。
|
1月前
|
测试技术 Python
Python自动化测试与单元测试框架
自动化测试在软件开发中扮演着重要的角色,可以减少人工测试的时间和成本,提高软件质量。而Python作为一种流行的编程语言,其丰富的库和框架可以让测试变得更加容易和高效。本文将介绍Python自动化测试和单元测试框架,包括unittest和pytest的使用方法、优点和缺点,以及如何根据项目需求选择合适的框架。