《测试驱动的嵌入式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的信息。

相关文章
|
3天前
|
设计模式 前端开发 JavaScript
自动化测试框架设计原则与最佳实践####
本文深入探讨了构建高效、可维护的自动化测试框架的核心原则与策略,旨在为软件测试工程师提供一套系统性的方法指南。通过分析常见误区,结合行业案例,阐述了如何根据项目特性定制自动化策略,优化测试流程,提升测试覆盖率与执行效率。 ####
20 6
|
3天前
|
人工智能 前端开发 测试技术
探索软件测试中的自动化框架选择与优化策略####
本文深入剖析了当前主流的自动化测试框架,通过对比分析各自的优势、局限性及适用场景,为读者提供了一套系统性的选择与优化指南。文章首先概述了自动化测试的重要性及其在软件开发生命周期中的位置,接着逐一探讨了Selenium、Appium、Cypress等热门框架的特点,并通过实际案例展示了如何根据项目需求灵活选用与配置框架,以提升测试效率和质量。最后,文章还分享了若干最佳实践和未来趋势预测,旨在帮助测试工程师更好地应对复杂多变的测试环境。 ####
17 4
|
9天前
|
机器学习/深度学习 前端开发 测试技术
探索软件测试中的自动化测试框架选择与优化策略####
本文深入探讨了在当前软件开发生命周期中,自动化测试框架的选择对于提升测试效率、保障产品质量的重要性。通过分析市场上主流的自动化测试工具,如Selenium、Appium、Jest等,结合具体项目需求,提出了一套系统化的选型与优化策略。文章首先概述了自动化测试的基本原理及其在现代软件开发中的角色变迁,随后详细对比了各主流框架的功能特点、适用场景及优缺点,最后基于实际案例,阐述了如何根据项目特性量身定制自动化测试解决方案,并给出了持续集成/持续部署(CI/CD)环境下的最佳实践建议。 --- ####
|
10天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
44 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
9天前
|
测试技术 API Android开发
探索软件测试中的自动化框架选择与实践####
本文深入探讨了软件测试领域内,面对众多自动化测试框架时,如何依据项目特性和团队需求做出明智选择,并分享了实践中的有效策略与技巧。不同于传统摘要的概述方式,本文将直接以一段实践指南的形式,简述在选择自动化测试框架时应考虑的核心要素及推荐路径,旨在为读者提供即时可用的参考。 ####
|
13天前
|
测试技术 开发者 UED
探索软件测试的深度:从单元测试到自动化测试
【10月更文挑战第30天】在软件开发的世界中,测试是确保产品质量和用户满意度的关键步骤。本文将深入探讨软件测试的不同层次,从基本的单元测试到复杂的自动化测试,揭示它们如何共同构建一个坚实的质量保证体系。我们将通过实际代码示例,展示如何在开发过程中实施有效的测试策略,以确保软件的稳定性和可靠性。无论你是新手还是经验丰富的开发者,这篇文章都将为你提供宝贵的见解和实用技巧。
|
11天前
|
jenkins 测试技术 持续交付
软件测试中的自动化测试策略
在当今快速发展的软件行业中,自动化测试已成为确保软件质量和效率的关键工具。本文将探讨自动化测试的重要性、实施策略以及面临的挑战,旨在为软件开发团队提供实用的指导和建议。
|
13天前
|
测试技术 Android开发 UED
探索软件测试中的自动化框架选择
【10月更文挑战第29天】 在软件开发的复杂过程中,测试环节扮演着至关重要的角色。本文将深入探讨自动化测试框架的选择,分析不同框架的特点和适用场景,旨在为软件开发团队提供决策支持。通过对比主流自动化测试工具的优势与局限,我们将揭示如何根据项目需求和团队技能来选择最合适的自动化测试解决方案。此外,文章还将讨论自动化测试实施过程中的关键考虑因素,包括成本效益分析、维护难度和扩展性等,确保读者能够全面理解自动化测试框架选择的重要性。
31 1
|
19天前
|
监控 安全 jenkins
探索软件测试的奥秘:自动化测试框架的搭建与实践
【10月更文挑战第24天】在软件开发的海洋里,测试是确保航行安全的灯塔。本文将带领读者揭开软件测试的神秘面纱,深入探讨如何从零开始搭建一个自动化测试框架,并配以代码示例。我们将一起航行在自动化测试的浪潮之上,体验从理论到实践的转变,最终达到提高测试效率和质量的彼岸。
|
21天前
|
机器学习/深度学习 数据采集 人工智能
探索AI驱动的自动化测试新纪元###
本文旨在探讨人工智能如何革新软件测试领域,通过AI技术提升测试效率、精准度和覆盖范围。在智能算法的支持下,自动化测试不再局限于简单的脚本回放,而是能够模拟复杂场景、预测潜在缺陷,并实现自我学习与优化。我们正步入一个测试更加主动、灵活且高效的新时代,本文将深入剖析这一变革的核心驱动力及其对未来软件开发的影响。 ###