嵌入式系统软件测试及测试案例开发
测试是传统软件开发的最后一步。整个软件开发过程,需要收集要求、进行高层次的设计、详细设计、创建代码、进行部分单元测试,然后集成,最后才开始最终测试。
最佳的开发实践应包含代码检查这个步骤。然而代码检查一般只能找出70%的系统错误,因此完美的测试环节绝对必不可少。测试就像个复式记帐系统,可以确保将缺陷扼杀在最终推出的产品之前。
在所有其它的工程实践中,测试都被视为基本环节。比如,在美国,每一座联邦政府出资修建的桥都必须经过大量的风洞测试。而在软件领域,测试并没有很受重视。尽管测试是所有工程实践准则的关键部分,但编写测试程序却感觉是在浪费时间。好在嵌入式系统设计界内的许多领域已经将测试作为其工作的核心部分,他们认识到将这个关键步骤放在项目末期极不明智,因而主张同步地编写测试程序和应用程序。
嵌入式系统软件测试在诸多方面都与应用软件测试一样。不过,应用测试与嵌入式系统测试之间还是存在一些重要差异。嵌入式开发人员一般会用到基于硬件的测试工具,而这类工具通常不会用于应用开发过程中。此外,嵌入式系统一般都有些独一无二的特性,这些特性应该在测试计划中得以体现。本文将介绍测试和测试案例开发的基础知识,并指出整个嵌入式系统测试工作的特有细节。
何时测试以及如何测试
从图1可以看出,在可行的条件下,测试应尽早展开。一般来讲,最早的测试是由最初的开发人员进行的模块或单元测试。遗憾的是,开发人员大多对如何建构一整套测试例程以进行测试所知不足。由于精心设计的测试例程通常直到集成测试时才能使用,因此许多在单元测试过程中就能找出的缺陷直到集成测试时才会被发现。比如,硅谷的一家大型网络设备厂商为找出其软件集成问题的关键原因,进行了一项研究。这家厂商发现,在项目集成阶段找出的缺陷中,有70%是由在集成之前从没被执行过的程序所产生的。
图1:改正问题的成本。
单元测试:开发人员在单独进行模块级测试时一般是编写存根代码(stub code)取代余下的系统软硬件。在开发周期的这个环节,测试主要侧重于代码的逻辑性能。
通常,开发人员会分别使用某些平均值、高值或低值、以及某些超出范围的值(以测试代码的异常处理功能)进行测试。但这些基于“黑匣子”的测试仅能对模块中整个代码的一部分进行测试。
回归测试:测试不应是一劳永逸的。每次修改程序后都应该重新进行测试,以确保这些更改不会无意中“误伤”某些不相关的行为。
称为回归测试的这类测试,一般是通过测试脚本自动进行的。比如,如果你设计了一组100个输入/输出(I/O)测试,回归测试脚本会自动执行这100个测试,然后将输出与一组“黄金标准”输出进行对比。每次对代码的任何部分进行修改时,都要对包含被修改代码的整个程序运行整套回归测试程序包,以确保修改过程中不会“误伤”其余代码。
测试什么
因为没有一个实际的测试集可以证明一个程序是正确的,因此关键问题变成了哪个测试子集最有可能检测到最多的错误。选择合适的测试例程的问题被称为测试例程设计。虽然存在数十种测试案例的设计方法,但它们通常可归为两种截然不同的方法:功能测试和覆盖测试。
功能测试(也称为黑匣子测试)选择可评估实现与需求规格符合程度的测试。覆盖测试(也称为白匣子测试)选择可执行代码某些部分的测试例程。(过后,将详细讨论这两种方法。)
这两种测试都是对嵌入式设计进行严格测试所必需的。其中,覆盖测试表示代码的稳定性,所以这种测试是用于已经完成或将近完成的产品的。另一方面,可在编写要求文档时,同时编写功能测试。
事实上,从功能测试开始入手,可以最大限度地降低重复劳动和重写测试案例的工作。因此,在我看来,要先考虑功能测试。
每个人都同意先编写功能测试这个观点,有人认为,功能测试在系统集成阶段(而不是在单元测试时)最有用。以下是整合功能测试和覆盖测试方法的一个简单处理流程:
找出哪些功能未被功能测试完全覆盖。
找出每个功能的哪些部分没被执行。
找出需要哪些额外的覆盖测试。
运行新增的额外测试。
重复以上步骤。
何时停止测试?
最通用的停止标准(按可靠性排序)如下:
老板命令停止测试
新的测试周期找到的新缺陷少于X个
在没有发现任何新缺陷的情况下已经满足了某个覆盖阀限
无论你多么彻底地测试了程序,都无法保证找出所有缺陷。这引发了另一个有趣的问题:你可容忍多少缺陷?假设在极端软件压力测试过程中,你发现系统每进行大约20小时的测试就会锁定。你仔细地检查程序,
- Baby-LIN应用案例集锦(12-16)
- CAN总线丢帧案例诊断经验(12-12)
- 单片机典型案例开发(三)(07-13)
- Android驱动开发入门及手机案例开发分析(07-25)
- 单片机典型案例开发(一)(05-28)
- 基于CPLD的驱动数码显示电路设计案例(03-17)