浅谈软件测试嵌入式单元测试技能

开发 测试
本文阐述了单元测试在嵌入式系统软件开发流程中的作用以及其如何帮助开发者处理上述疑问。简要地讲,它提升了桩函数在宿主环境下或者仿真器中的作用。这使软件工程师在代码编写完毕之后就可以立即对其执行 验证,即使此时目标硬件系统还没有准备好或暂时不能执行测试。

单元测试是在软件开发过程中要进行的最低级别的测试活动,在单元测试活动中,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。 单元测试不仅仅是作为无错编码一种辅助手段在一次性的开发过程中使用,单元测试必须是可重复的,无论是在软件修改,或是移植到新的运行环境的过程中。因此,所有的测试都必须在整个软件系统的生命周期中进行维护。

对嵌入式系统软件执行测试是一项很有挑战性的工作。因为嵌入式系统的软件开发平台和最终运行平台是完全不一样的两个平台,开发者不可能在其运行平台上像在桌面环境那样执行测试程序。这些差异可以表现在这多个方面。在软件开发的时候目标硬件平台可能还没有准备好,软件工程师在访问硬件时可能会很麻烦,在开发环境下模拟整个系统存在困难性等。

本文阐述了单元测试在嵌入式系统软件开发流程中的作用以及其如何帮助开发者处理上述疑问。简要地讲,它提升了桩函数在宿主环境下或者仿真器中的作用。这使软件工程师在代码编写完毕之后就可以立即对其执行 验证,即使此时目标硬件系统还没有准备好或暂时不能执行测试。这样,绝大部分的潜藏在程序中的逻辑疑问都能够在早期被发觉,软件工程师就可以迅捷地修正这些疑问,而目标平台上的测试就可以注重于检测软硬件接口方面的疑问。

为何要对嵌入式系统软件执行单元测试?

单元测试是检查嵌入式系统软件缺陷的最有效的一种要领,更确切而言也即在宿主机环境下或在仿真器上执行 的API测试。这能使测试尽早开始并且最大程度地降低了测试工作对目标硬件平台的依赖性。嵌入式系统软件的单元测试的一个前提是能够让软件独立于硬件执行 测试,这是由桩函数对目标硬件平台的模拟而实现的。在这种情况下,代码的绝大多数功能性测试都能够独立于目标硬件系统执行 测试。这对嵌入式系统开发者而言有以下主要优点。

1. 单元测试能让开发者在目标硬件平台准备好之前就开始测试周期,开发者可以直接在宿主开发环境下开始原始测试(而不须要目标硬件平台)。早期测试能够为开发团队发觉并修正缺陷提供尽可能充足的时间。此外,早期测试还能将测试工作较平均地分配于产品开发的各个阶段中,从而防止将所有的测试工作留到产品揭晓之前才执行 ,为开发团队争取了充足的时间。

2. 单元测试采取一种“各个击破”的策略,允许用户将复杂的系统分为相对较基本的准独立系统执行 测试。测试工具能够管理模块之间的关联性并运用桩函数来模拟这些模块的行为。

图1:运用桩函数,开发者可以模拟真实环境和程序之间的相互影响对程序的某个模块执行 测试,而不须要目标硬件平台以及暂时还未完成的其它代码模块。在测试环境中,桩函数充当了为待测模块提供外部关联性桥梁的作用。

在代码修改流程中保卫代码完整性

复杂系统的开发者最大的烦恼就是不能确定对代码的修改能不能改动或破坏了程序的既有功能性。为了打消这些顾虑,用户可以建立一个基准单元测试套件来捕捉代码的既有功能性。如果用户希望检查相对于基准单元测试套件的改动,只须要对修改过的代码定期地运行该测试套件即可。因为单元测试能够独立地测试系统的某些代码,所以这样一组回归测试套件可以持续地执行而无论目标硬件平台准备好与否。这种测试并不排斥对整个程序执行 测试的分离回归测试套件。

这样的测试套件作为变更检测完全能够让用户确保如果无意间破坏了既有功能性能够被立即告知。当目标硬件平台准备好后,用户可以直接用宿主环境下的测试来验证代码能不能会在实际的目标硬件平台上正确地运行。虽然代码在宿主环境下执行 了回归测试,但是在目标硬件平台上还是须要执行 一定的自动系统测试。

验证不正确处理

由于消费类产品的不正确处理可靠性需求的不同,所以系统测试情景变得更加复杂了。因为不能精确地预测其运用情况,所以系统的设计和测试都不能基于某个特定的情况。

相反这些系统必须经过广泛地不正确以及未预期输入的验证,以保证这些输入能够被正确地处理。

运用含有桩函数的单元测试同样能够大大地简化不正确测试流程。总的而言,在运用 程序这一层级来测试不正确条件是一件相当耗时的事,因为将程序置于“正确的不正确状态”须要准备相对复杂的输入数据并且同时要求程序在其大量的可能状态中处于相应的状态。与之形成鲜明比较的是,运用 “不正确模拟”要领来对某些函数执行 不正确处理测试则要基本得多。

例如,测试一个含有输入数据不正确处理机制的测试是相当基本的:

  1. float signalToNoiseRatio (float signal,float noise,MODE mode)  
  2. {  
  3. if (MODE_MEASUREMENT == mode)  
  4. {  
  5. …  
  6. if (signal < 0 noise < 0)  
  7. {  
  8. handle_bad_data();  
  9. }  
  10. }  
  1. float signalToNoiseRatio (float signal,float noise,MODE mode)  
  2. {  
  3. if (MODE_MEASUREMENT == mode)  
  4. {  
  5. …  
  6. if (signal < 0 noise < 0)  
  7. {  
  8. handle_bad_data();  
  9. }  
  10. }  
  11. }  

在这种情况下,在测试程序中对handle_bad_data()函数的调用是很基本的,因为测试该语句能不能受程序输入数据控制是很基本的。

尽管如此,很多情况下控制语句并不直接受控于函数接口,而是取决于系统处于的特定状态,如下例所示。将系统置于该不正确状态可能会相当复杂,甚至可能牵扯到让设备接口处于某些特定状态,所以测试用例中的这个条件就须要通过模拟来实现。

  1. float shutDown ()  
  2. {  
  3. if (uploadingData())  
  4. {  
  5. userMessage (“Cannot execute shutdown while uploading data”);  
  6. recoverShutDown();  
  7. }  
  8. else 
  9. {  
  10. // shut down indeed  
  11. }  
  12. }  

运用支持“智能桩函数”(smart stubs)的高级测试工具,对于复杂不正确条件的测试比其它要领就基本了很多。“智能桩函数”允许通过原始函数的桩函数以及通过实现某些必要的特定功能的要领来执行代码。实际而言,当被测程序明显没有处于不正确状态时,其相应待测函数将被调用以模拟不正确实际发生的情况,这就是“不正确模拟”的意义。在上述例子中,对不正确处理函数uploadingData()的测试须要其桩函数以保证至少让其返回一次“真”。

一些值得考虑的疑问

在宿主环境下执行 测试可能意味着用以建立代码的编译器和目标系统编译器不同。如果交叉编译器提供商同时提供用在宿主环境下的编译器(例如Green Hills Software的本地编译器),则可以直接运用。如果没有,则可以运用支持大多数平台的GNU Compiler Collection(GCC)。虽然保证代码在宿主编译器和目标编译器上的一致性会对维护成本有些许的提升,但是与早期测试对整个项目带来的优点相比却是很合算的。

单元测试不大可能发觉因同步不正确造成的运用 程序级的不正确条件或者与实际设备接口的不正确。尽管如此,在嵌入式系统的开发流程中,单元测试能帮助开发者尽早地发觉很多类不正确,所以提高了系统整体开发效率并且消除了测试瓶颈。

运用 C++test来自动化单元测试流程

用户可以运用 Parasoft C++test来自动化对嵌入式系统软件的测试。

Parasoft C++test是一套经广泛验证的最佳编码实践的自动化处理方案,它能有效地提高软件开发团队的开发效率以及软件的质量。C++test能帮助用户执行 编码策略增强、静态代码分析、彻底代码走查以及单元和组件测试,从而为用户保证其C以及C++代码按预期运行提供一个实际可行的要领。C++test可以在桌面环境下的主流IDE(包括Wind River Workbench以及Eclipse)以及回归测试流程中的命令行中以批处理的方式运行。C++test集成了Parasoft的GRS报告系统,它能提供一个基于Web交互界面的报表并为用户提供向下挖掘(drill-down)功能,基于C++test报告的这些结果,开发团队实时把握项目状态和趋势以及其它关键指标。

对于嵌入式以及交叉开发而言,C++test可以在基于宿主环境和目标环境下执行代码分析以及数据流分析。在宿主环境中,开发者可以通过C++test的编码策略增强、静态代码分析、彻底代码走查以及单元和组件测试模块来对代码执行 “随时测试”式的验证以及回归测试。被测代码的外部依赖性被桩函数自动地取代,桩函数能真实地模拟硬件以及其它代码在实际运行中的表现。

图2:Wind River Workbench中的C++test插件能为其用户提供一个简便的要领来运用 C++test执行 完整的代码分析和单元测试。

通过扩展的基于宿主环境的测试,C++test能让用户在某个模块代码完成后立即对其执行 测试,即使此时目标环境还未准备好或不能执行 测试。这样,绝大部分的潜藏在程序中的逻辑疑问都能够在早期被发觉,从而让开发者修正这些不正确变得十分迅捷,而在目标硬件平台上的测试则能更加注重于验证软硬件接口疑问。此外,基于宿主环境的测试相对于目标系统而言更加容易自动运行和维护,这使得用户可以不用其它嵌入式开发工具就可以验证相对平台独立的代码正确性。

当开发者在仿真器或实际目标硬件平台上执行 测试时,在宿主环境下生成和优化过的测试套件可以被重用来验证软件在目标硬件上的功能性。先前的桩函数这时可以用实际代码或者系统接口来代替从而完成完整系统的测试,而不须要修改测试代码。C++test同时还提供一个内建功能来自动捕捉执行测试输出以及将其转变成后续回归测试时的“黄金”数据集。

C++test让整个测试流程自动执行 ,包括测试用例生成、交叉编译、部署、执行以及将测试结果(包括覆盖率指标)导入GUI。测试可以通过GUI交互地执行 或者通过命令行自动执行,以及以批处理的方式执行 的回归测试。在交互模式下,用户可以对单个模块或者一组代码执行 测试,从而让调试和验证都变得更基本。在批处理模式下,测试可以针对用户提供的代码或者根据文件名字或在硬盘中的位置来执行 。

同时,C++test还支持将其执行顺序执行 完全的用户自定义。除了内建的自动化测试,用户还可以引入自定义测试脚本以及通过shell命令将测试工具根据代码具体结构和测试环境执行 自定义。C++test的运行库也可以被自定义并且针对不同的目标操作系统执行 交叉编译。这种无可比拟的灵活性能够任意实现其希望的测试流程而不须要预设工具的功能。

图3:C++test的可自定义工作流程让用户可以根据代码的构造来执行 测试,然后运用这个测试套件在目标硬件环境下测试其功能性和可靠性。

嵌入式开发支持的单元测试

真实的单元(函数/类)和组件测试

自动生成C或C++格式的测试套件

支持数据源

单一测试或者任意测试的任意组合方式的交互执行

捕捉实际测试结果后自动生成回归测试套件

宿主平台和目标平台的统一测试环境

测试流程完全可自定义并提供运行时支持

语句、代码块、分支/条件以及路径指标覆盖率分析

提供HTML和XML格式的测试结果报告

支持GUI/桌面环境以及命令行方式

支持的目标编译器

Wind River GCC 3.4.x和DIAB 5.4+

GCC 2.95.x – 4.1交叉编译器

Green Hills 4.0.x

【编辑推荐】

  1. 初探PHP单元测试利器:PHPUnit
  2. PHP单元测试利器:PHPUnit深入用法
  3. PHP单元测试利器:PHPUnit深入理解
  4. Visual Studio 2008单元测试之数据库测试
责任编辑:于铁 来源: 51Testing
相关推荐

2011-04-18 11:34:34

嵌入式软件测试

2011-06-14 15:56:42

单元测试

2011-06-20 17:25:02

单元测试

2017-01-14 23:42:49

单元测试框架软件测试

2020-08-18 08:10:02

单元测试Java

2017-01-16 12:12:29

单元测试JUnit

2017-01-14 23:26:17

单元测试JUnit测试

2011-05-16 16:52:09

单元测试彻底测试

2023-07-26 08:58:45

Golang单元测试

2017-03-23 16:02:10

Mock技术单元测试

2011-07-04 18:16:42

单元测试

2020-05-07 17:30:49

开发iOS技术

2021-05-05 11:38:40

TestNGPowerMock单元测试

2022-08-11 16:37:55

单元测试代码

2011-12-01 09:20:41

软件工程

2024-10-16 16:09:32

2022-08-02 08:07:24

单元测试代码重构

2009-09-29 16:21:31

Hibernate单元

2016-09-21 15:35:45

Javascript单元测试

2022-05-12 09:37:03

测试JUnit开发
点赞
收藏

51CTO技术栈公众号