文章目录
本文译自Unity/docs/UnityGettingStartedGuide.md,以及自己使用的总结。
Unity的官网:ThrowTheSwitch.org。
获取最新版本的Unity,可以访问:ThrowTheSwitch/Unity。
一、欢迎
恭喜,你现在自豪的拥有了自己的一堆bit。你准备对这些0和1做些什么呢?这篇文章将帮助你做相关的决定。
Unity是一个单元测试框架,目标是精简而强大。Unity的内核只有三个文件:一个C文件和两个头文件!它们相互合作提供了许多函数和宏使得测试更容易。
Unity的设计是跨平台的。它尽可能地遵从C标准,同时提供对许多不守规矩的嵌入式C编译器的支持。Unity已被用在许多编译器上,包括GCC、IAR、Clang、Green Hills、 Microchip和MS Visual Studio,将其移植到一个新目标编译器上也不需要太多工作量。
二、文档概览
2.1 Unity入门指南
对应docs/UnityGettingStartedGuide.md,也就是本篇译文。讲解了整个Unity工程目录结构,以及帮助文档概要说明,最后以示例讲解了Unity的简单使用。
2.2 Unity断言参考文档
对应docs/UnityAssertionsReference.md。这篇文档将带你看遍Unity提供的所有断言选项,这将是你单元测试的面包黄油。你将在断言上花费比Unity其他部分更多的时间。
2.3 Unity断言欺骗表
对应docs/UnityAssertionsCheatSheetSuitableforPrintingandPossiblyFraming.pdf。正如文档命名,它是Unity断言参考文档中所有断言的摘要,专门用于打印出来在使用中参考。
2.4 Unity配置向导
对应docs/UnityConfigurationGuide.md。当你需要在新的平台或编译器上使用Unity,需要查看这篇文档。这篇文档详细说明了Unity的所有配置选项,你可以根据你的测试需求定制相关的配置选项。
2.5 Unity辅助脚本
对应docs/UnityHelperScriptsGuide.md,该文档描述了如何利用辅助脚本来简化你的测试工作。这些辅助脚本使用Ruby编写,存放在在auto文件夹中。单独使用Unity并不会用到辅助脚本,它们只是用来简化使用Unity的测试工作而已。
2.6 Unity许可
没有许可证文件的开源项目会是什么?因此,Unity也有License,它描述了你使用Unity时应该同意的条款。我们希望你无论在什么情况下使用,Unity对你都是非常有用的;但如果在使用中遇到了问题,请不要责备我们。
三、文件夹概述
如果你已经通过Github或者其他类似地方获取到了Unity,你可能会被Unity展现的文件吓一跳。别担心,Unity本身是非常小的(前面讲了Unity核心只有三个文件),其他部分仅仅是为了让你的生活变得更好(使用更加简单)而存在。是否忽视还是使用它们,全凭你自己做主。下面是Unity工程的概述:
- src:这就是你关心的代码,它包含一个C文件和两个头文件,这就是Unity;
- docs:所有使用方法的文档都放在这里,包括你现在正在阅读的文档;
- examples:存放了一些Unity的使用示例;
- extras:这里存放了Unity的外部插件,它们不是Unity工程的核心;如果你是通过阅读James Grenning获取的Unity,你会想了解它;
- test:这里包含了Unity本身和相关脚本的全部测试。如果你只是使用Unity,这个文件夹你永远不会用到;但如果你是团队中负责将Unity移植到新的工具链的人,这里的测试可以帮助验证你的移植是否都配置好了;
- auto:你将在这里发现一些可以简化测试工作的Ruby脚本。对于仅是使用Unity来讲,它们完全是可选的。
四、如何创建测试文件
4.1 创建并包含头文件
测试文件都是C文件。大部分情况下,你需要为你的每一个C模块创建一个测试文件,并且这个测试文件需要包含unity.h和C模块对应的头文件。
#include "unity.h"
#include "file_to_test.h"
4.2 实现setUp()和tearDown()
接着,一个测试文件需要包含setUp()和tearDown()函数:setUp()函数可以包含每个测试之前需要运行的任何东西;tearDown()函数则包含每个测试之后需要运行的任何东西;这两个函数都不接受任何参数,也不返回任何值。
void setUp(void)
{
// set stuff up here
}
void tearDown(void)
{
// clean stuff up here
}
++如果你的测试不需要在用例前后做任何事情,你可以保留这两个函数实现为空;如果你移植Unity时,将这两个函数配置为可选的,那么你可以完全不实现它们++。如果不确定现有环境这两个函数是否可选的,试一下就知道了:如果编译器在链接时抱怨说找不到setUp()和tearDown()函数实现,那么就知道需要为它们创建一个空实现了。
4.3 添加测试函数
测试文件的主题是一系列的测试函数。测试文件遵循如下命名规则:以“test_”或spec_开头。当然,你也可以不这样命名,但这样会使其他开发者更加容易看出来哪些是测试函数;而且,Unity和Ceeling中的自动化脚本默认也是去寻找带这种前缀的测试函数。测试函数都不接受任何参数,也不返回任何值。所有测试计数(成功或失败)都由Unity内部处理。
void test_function_should_doBlahAndBlah(void) {
//test stuff
}
void test_function_should_doAlsoDoBlah(void) {
//more test stuff
}
4.4 实现主程序main()
最后,在你的测试文件的最后,实现测试主程序main():这个主程序首先调用UNITY_BEGIN(),接着为每个测试调用RUN_TEST(),最后调用UNITY_END()。RUN_TEST()将会触发每个测试函数的运行,所以,你一定要为你的每一个测试调用对应调用RUN_TEST()。
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_function_should_doBlahAndBlah);
RUN_TEST(test_function_should_doAlsoDoBlah);
return UNITY_END();
}
记住为每个测试都要在主程序main()中添加RUN_TEST()的过程比较单调乏味。如果你喜欢在构建过程中使用辅助脚本,那么你可以考虑使用我们++提供的generate_test_runner.rb脚本:它将自动创建main()主程序,并自动将你的所有测试包含进主程序中++,前提是你遵循了4.3节介绍的测试函数命名规范。在这种情况下,你的测试文件中就不需要实现main()主程序了。
4.5 完整的示例
通过前面的介绍,已经基本满足你的需求了。若想要获取更多的信息,可以查看第二章介绍的相关文档。下面是前面讲述示例的完整代码:
#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
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_function_should_doBlahAndBlah);
RUN_TEST(test_function_should_doAlsoDoBlah);
return UNITY_END();
}
五、如何构建并运行测试文件
在C和C++这样的语言中,学习一个新的单元测试框架,是一个非常大的(single biggest)挑战。C和C++这样的语言很擅长让你“接近金属”(注:意思是接近硬件本质)(为什么要用金属这个词?“接近硅”不是更加准确么?),语言这样的特性通常是非常不错的,不过这也给测试带来了更大的挑战。
对于不同的工具链,你有两个好的选择:在模拟机上运行,还是在电脑上运行。这取决于你来自何方(注:当前开发环境的条件)?你可能会感到惊讶:居然没有在目标硬件上运行的选项!这是有很多原因的,比如:
- 在硬件上,你有太多的限制(如处理能力、内存等);
- 在硬件上,你无法完全控制所有的寄存器;
- 在硬件上,单元测试更加具有挑战;
- 单元测试并不是系统测试,保持它们的独立性。
大部分开发者选择将单元测试作为本地应用程序(比如使用gcc或MSVC)或模拟器上的应用程序来运行,而不是在真正的硬件上运行。它们都是不错的选择:本地应用程序的优势是配置起来更加简单、快捷;模拟器上的应用程序的优势是使用了与你目标硬件相同的编译器。关于这两个方式的配置项,详见Unity配置向导:UnityConfigurationGuide.md。
为了使这两种方式正常运转,你也许需要根据你的目标硬件,对寄存器集文件(比如类型宽度)做一些配置(后面将会讨论)。
构建测试程序,需要链接Unity、测试C文件和对应被测试的C文件,最终会生成一个可执行文件,这个可执行文件就是被测文件对应模块的单元测试集合。对不同的测试模块和测试文件,重复这个过程,分别生成不同的单元测试集合的可执行文件。
将单元测试分离到独立的可执行文件是非常灵活的,这样允许我们:
- 对我们的系统进行更全面、更彻底的单元测试;
- 保持所有的测试代码与我们最终发布的产品代码分离。