嵌入式软件单元测试

67

在嵌入式系统中,您使用了哪些最佳实践来进行单元测试?

10个回答

58

嵌入式软件在过去的10年里已经取得了长足的进步,但我们通常会执行以下操作:

  • 对于不依赖于目标硬件的算法,我们只需在非嵌入式平台上构建并测试单元测试。
  • 对于需要硬件支持的内容,我们将单元测试有条件地编译到代码中,以使用可用的硬件。在我们的情况下,是目标串口将结果推送到另一台更强大的机器,在那里检查测试的正确性。
  • 根据硬件的不同,您有时可以在非嵌入式平台上模拟“虚拟”设备。这通常包括另一个执行线程(或信号函数)更改程序使用的内存。对于内存映射I / O很有用,但对于IRQ等不适用。
  • 通常,您每次只能单元测试完整代码的一小部分(由于内存限制)。
  • 对于时间敏感的测试,我们不进行测试。简单明了。我们使用的硬件(8051和68302)如果运行速度太慢,可能无法正常工作。这种调试必须使用CRO(示波器)和(当我们有更多资金时)ICE(电路仿真器)进行初始调试。

希望自那时以来情况有所改善。我不会希望这种痛苦发生在我的最大敌人身上。


1
根据我的了解,那听起来非常像目前的技术水平。至少,在过去一年左右与TI TMS320合作的经验中是如此。 - JustJeff
1
你的意思是这些列出来的方法都是“最先进的”,我希望是这样。当然,没有人仍在使用8051(68302还可以,因为我对Motorola 68k有美好的回忆——在我看来,它仍然是比x86更清晰的架构)?我曾经希望所有新的嵌入式开发都是在Intel克隆机上完成的,因为有大量的开发选项。 - paxdiablo
6
今天正在构建和设计的系统中,有大量采用8051微控制器的系统,甚至还有更多采用PIC的系统,这两者的架构和性能水平与现代的8051非常相似。 - Mark
我赞同在非嵌入式环境中测试算法的想法。这为我节省了大量的工作(特别适用于通信编码/解码、传感器ADC到工程单位计算等)。这似乎是很多书应该写的东西...(MatthewRankin的回答看起来很有趣)。 - Dmitri

25

在PC环境下进行单元测试(使用PC C编译器编译代码并在PC单元测试框架中运行代码)会有很多好处,但需要注意以下几点:

  1. 这不适用于测试低级别的代码,包括启动代码、RAM测试和硬件驱动程序。您需要使用更直接的单元测试来测试这些内容。
  2. 您嵌入式系统的编译器必须是可靠的,以避免寻找由编译器引起的错误。
  3. 您的代码必须具有分层结构,具有硬件抽象层。您可能需要为PC单元测试框架编写硬件驱动程序模拟器。
  4. 您应始终使用 stdint.h 类型,例如 uint16_t 而不是普通的 unsigned int 等。

我们遵循了这些规则,并发现在PC单元测试框架中对应用程序层代码进行单元测试后,我们可以有很高的信心它能够正常工作。

在PC平台上进行单元测试的优点:

  1. 您不会因为添加单元测试框架而在嵌入式平台上耗尽ROM空间。
  2. 编译-链接-运行循环在PC平台上通常更快,更简单(避免了可能需要几分钟的“编写/下载”步骤)。
  3. 您有更多选项来可视化进度(一些嵌入式应用程序具有有限的I/O外设),存储输入/输出数据以进行分析,运行需花费更多时间的测试。
  4. 您可以使用现成的基于PC的单元测试框架,这些框架在嵌入式平台上不可用或不适用。

16

1
我刚刚买了这本书。 我现在要进入嵌入式领域,想使用Microchip C30进行单元测试,但我遇到了一些困难。 - Daniel Grillo

16

嵌入式系统是一个广泛的话题,但总体而言,可以将其视为结合了硬件和软件的特定目的产品。我在移动电话领域有嵌入式背景,这只是所有嵌入式系统的一小部分。以下几点我会尽量讲得抽象一些:

  • 尽可能地抽象出硬件依赖性。这样,您可以在模拟的“硬件”上运行单元测试,并且还可以测试各种罕见/异常情况,在目标设备上测试会更加困难。为了避免抽象成本,可以使用条件编译等方法。

  • 尽可能少地依赖于硬件。

  • 在模拟器或交叉编译器环境中运行的单元测试仍然不能保证代码在目标硬件上正常工作。您必须在目标设备上进行测试。尽早在目标设备上进行测试。


2
我会补充一点:“尽早在目标设备上进行测试。”如果是定制硬件或带有重要定制组件的硬件,则需要更加重视。 - Vicky

7

作为一名缺乏经验的人,但最近我也在考虑这个问题。我认为最好的方法应该是:

A)在PC环境中尽可能多地编写与硬件无关的应用程序代码,并同时编写单元测试(在PC上进行此操作应有助于强制分离与硬件无关的内容)。这样你就可以使用你选择的单元测试工具,然后通过RS-232和/或示波器和I/O引脚发出时间依赖数据的信号来测试与硬件相关的内容。

B)将所有内容都写在目标硬件上,但是有一个make目标可以有条件地编译单元测试构建,以通过RS-232或其他方式运行单元测试并输出结果(或可用于分析结果的数据)。如果内存不足,这可能会很棘手。

编辑7/3/2009 我刚刚想到了如何对硬件相关内容进行单元测试的另一个想法。如果您的硬件事件发生得太快,无法通过RS-232记录,但您又不想手动筛选大量示波器数据以检查I/O引脚标志是否按预期上升和下降,则可以使用带有集成DIO的PC卡(例如National Instruments的数据采集卡系列)自动评估这些信号的时间。然后,您只需要编写在PC上运行的软件来控制数据采集卡与当前正在运行的单元测试同步。


7
我们成功地使用模拟器来测试涉及硬件的代码,我们使用Keil的模拟器和IDE(没有关联,只是使用了他们的工具)。我们编写模拟器脚本以驱动“硬件”,并且能够相当可靠地测试我们的工作代码。尽管对于某些测试需要模拟硬件,但对于大多数情况,这种方法非常有效,并且允许我们在没有任何实际硬件的情况下完成大量工作。我们甚至能够在模拟器中获得接近完整的系统功能,并且一旦将代码放到真正的硬件上,就很少出现问题。这也可以显著加快代码的生产速度,因为可以在PC上使用更深入的调试器,在模拟芯片时完成所有工作,而不是尝试在硬件上完成所有工作。
我们已经成功地在复杂控制系统、存储器接口、定制SPI驱动的IC甚至单色显示器上进行了测试。

5

这里有很多好的答案,但未提及的一些事情是要运行诊断代码,以便:

    记录HAL事件(中断、总线消息等)
    编写代码来跟踪您的资源(所有活动的信号量、线程活动)
    拥有一个捕获RAM机制,将堆和内存内容复制到持久性存储(硬盘或等效物)中,以检测和调试死锁、活锁、内存泄漏、缓冲区溢出等。

3
去年我面对这个问题时,我真的很想在嵌入式平台上进行测试。我正在开发一个库,并使用RTOS调用和其他嵌入式平台的功能。没有什么特别可用的,所以我适应了UnitTest++代码来满足我的需求。我在NetBurner系列上编程,由于它有一个嵌入式Web服务器,因此编写基于Web的GUI测试运行器相当简单,可以提供经典的红/绿反馈。结果非常好,现在单元测试变得更加容易,我感到更加自信,知道代码在实际硬件上运行良好。我甚至使用单元测试框架来进行集成测试。起初,我模拟/存根硬件并注入该接口进行测试。但最终,我编写了一些人在环测试,以操作实际硬件。结果证明,这是了解硬件的更简单的方法,并且具有从嵌入式陷阱中轻松恢复的简单方式。由于所有测试都是从AJAX回调到Web服务器运行的,因此陷阱只发生在手动调用测试的结果,并且系统始终在陷阱几秒钟后干净地重新启动。
NetBurner足够快,使得编写/编译/下载/运行测试循环大约需要30秒。

1

许多嵌入式处理器都可以在评估板上使用,因此尽管您可能没有真正的输入/输出设备,但通常可以在这些设备之一上执行大量算法和逻辑,通常可以通过jtag进行硬件调试。而“单元”测试通常更多地涉及您的逻辑而不是您的输入/输出。问题通常是如何将测试工件从这些环境中取回。


1

将代码分为设备相关和设备无关两部分。无关代码可以进行单元测试,不会太麻烦。相关代码需要手动测试,直到你拥有一个流畅的通信接口。

如果你正在编写通信接口,我很抱歉。


编写通信接口有难度吗? - abunickabhi
@abunickabhi - 有困难和繁琐/令人沮丧之分。当你通过LED调试时,构建一个可工作的设备相关驱动程序可能会非常乏味。 :) - Paul Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接