1 总体介绍
Unity项目git网址:https://github.com/ThrowTheSwitch/Unity
Unity是一个单元测试框架,Unity设计者团队的目标是让它保持小型化和功能性。核心的Unity测试框架只有三个文件:单个C文件和两个头文件。Unity提供了函数和宏,使得单元测试更加容易。
Unity被设计成跨平台,它努力坚持C标准,同时仍然支持许多违法规则的嵌入式C编译器。Unity可以在许多编译器环境中使用,比如GCC,IAR,Clang,Green Hills,Microchip, MS Visual Studio。适配Unity在一个新的目标平台协同工作并不需要很多工作量。
1.1 Unity的文档
- Unity Assertions reference 断言参考文档
该文档将指导你完成Unity提供的所有断言选项。相比Unity的其他部分,你将在的断言上花更多的时间。
- Unity Assertions Cheat Sheet 断言备忘清单
本文档包含了前一篇文档Unity Assertions reference的简短摘要。当你熟悉Unity选项以后,它非常适合打印出来,用于随时翻阅引用。
- Unity Configuration Guide 配置指导
如果您要将Unity用于新的目标平台或者编译器时,可以参考本文档,完成配置选项,定制测试实践,来满足你的需求。
- Unity Helper Scripts 帮助脚本
该档描述了可用于简化测试工作流的帮助程序脚本。它描述了Unity安装的auto目录中包含的可选Ruby脚本的集合。Ruby和这些脚本都不是使用Unity所必需的。它们是为那些希望使用它们的人提供便利的。
- Unity License 许可
什么是没有许可证文件的开源项目?该简要文档描述了您在使用Unity时同意的条款。基本上,Unity团队希望Unity在任何情况下对你有用,但是如果你遇到问题,请不要责怪他们
1.2 Unity项目目录结构
如果你通过Github或类似的东西获得了Unity项目,你可能会惊讶于Unity怎么有那么多东西。别担心,Unity本身是很小的。
其他的文件夹和文件只是让你的生活更轻松。你可以忽略它或者在你方便的时候使用它。以下是项目中所有内容的概述。
-- src - src里面就是你关心的代码,此文件夹包含一个C文件和两个头文件。这三个文件就是Unity 单元测试框架了。
-- docs - docs里面可以找到所有的文档
-- example - 包含了少量的Unity使用示例
-- extras - extras不是Unity项目的核心部分,是可选的附加项。如果读者是通过James Grenning(TDD,极限编程,敏捷开发等等)的书找到Unity这个项目的,可以看一下此部分。
-- test - 此文件夹包含了Unity框架是如何被Unity团队测试然后发布给社区的。如果读者仅仅是使用Unity,完全不用看这个文件夹。如果是Unity的代码贡献者,test文件夹是验证所有配置是否正确的好地方。
-- auto - 包含了有用的Ruby脚本,可以用于简化测试流程。此部分是可选的,如果使用Unity,不要求此部分。
2 如何创建一个测试文件
- 包含头文件
测试文件是C文件。通常,您将为要测试的每个C模块创建一个单独的测试文件。测试文件应该包括unity.h和要测试的C模块的头文件。
- seUp和tearDown函数
测试文件将会包含一个setUp()和一个tearDown()函数。setUp函数可以放一些你需要在执行每个test之前执行的代码。tearDown()函数可以放一些你需要在每一个test执行完以后执行的代码。setUp和tearDown函数不需要传入参数,不用返回值。如果不需要他们,可以是这两个函数的函数体为空白。
如果不需要这两个函数,不能直接不定义,这样编译器链接时会报错:找不到setUp或者tearDown定义。必须要定义这两个函数,如果不用,可以保持函数体空白。如果您的编译器抱怨在链接时找不到setUp或tearDown,那么您将知道至少需要为此包含一个空函数。
- 以test或者spec开头的测试函数
测试文件的大部分是一系列的测试函数,测试函数遵循以单词“Test”或“spec”开头的约定。您不必这样命名它们,但这种命名规则清楚地表明了哪些函数是其他开发人员的测试。另外,Unity或Ceedling附带的自动脚本将默认寻找以这种方式作为前缀的测试函数。测试函数不带参数,也不返回任何值。所有的测试认为都是在Unity内部处理的。
- main函数
最后,在测试文件的底部,写一个main()函数。在main()函数中将会调用UNITY_BEGIN(),然后调用RUN_TEST来测试,最后调用UNITY_END()函数。这将实际触发每个测试函数的运行,所以每个函数都有自己的“RUN_TEST”调用是很重要的。
测试文件的C语言示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include "unity.h" #include "file_to_test.h" void setUp(void) { // set stuff up here } void tearDown(void) { // clean stuff up here } void test_function_should_doBlahAndBlah(void) { //test stuff } void test_function_should_doAlsoDoBlah(void) { //more test stuff } // 如果使用Ruby脚本 generate_test_runner.rb,则不需要定义main函数 int main(void) { UNITY_BEGIN(); RUN_TEST(test_function_should_doBlahAndBlah); RUN_TEST(test_function_should_doAlsoDoBlah); return UNITY_END(); } |
2.1 运行测试函数
在main()函数中调用RUN_TEST来执行测试函数有两种方法:
1 2 3 4 | // 1. 从func的第linenum行开始执行 RUN_TEST(func, linenum); // 2. 从func的开头开始执行 RUN_TEST(func); |
这些宏在调用测试之前执行必要的设置,然后处理清理和结果制表。
2.2 忽略测试函数
有时候一个测试函数是不完整的,或者因为某些原因不合法。这个时候可以调用TEST_IGNORE,控制可以立即从测试函数返回到测试函数的调用者,并且不会返回失败。
1 2 3 4 | // 忽略该测试函数,立可返回 TEST_IGNORE(); // 或者,输出一个消息声明测试被忽略的原因 // TEST_IGNORE_MESSAGE (message); |
3 编译和执行一个测试文件
对于工具链,有两个非常好的选择(大多数开发人员不在实际硬件上运行测试)。
(1)将程序开发为native应用程序(例如使用gcc或MSVC)
(2)将程序写成在模拟器上运行的应用程序
两者都是不错的选择。native应用程序的优点是更快更容易设置。模拟器应用程序的优点是可以使用与目标应用程序相同的编译器。配置指南中讨论了这些配置的选项。
根据您来自何处,您可能会惊讶这两个选择都没有在您的硬件上运行单元测试。原因有很多,但这里有一个简短的版本:
- 在硬件上,您有太多的限制(处理能力、内存等)
- 在硬件上,你不能完全控制所有寄存器,
- 在硬件方面,单元测试更具挑战性,
- 单元测试不是系统测试。请把它们分开。
在任何一种情况下,测试都是通过链接unity、测试文件和被测试的C文件来构建的。这些文件可以构建一个可执行二进制,作为该C模块的测试集。这一过程被重复,构建下一个测试文件。
这种将测试分离为单独的可执行文件的灵活性使我们能够更彻底地对系统进行单元测试,并将所有测试代码排除在最终版本之外!
This flexibility of separating tests into individual executables allows us to much more thoroughly unit test our system and it keeps all the test code out of our final release!
Unity项目git网址:https://github.com/ThrowTheSwitch/Unity