单元测试嵌入式软件
您在对嵌入式系统特有的嵌入式软件进行单元测试时使用了哪些最佳实践?
What best practices have you used in unit testing embedded software that are peculiar to embedded systems?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
嵌入式软件在过去 10 年中可能取得了长足的进步,但我们通常做了以下工作:
希望自上次以来情况有所改善。 我不希望我最大的敌人承受这种痛苦。
Embedded software may have come a long way in the last 10 years but we generally did the following:
Hopefully the situation has improved since I last did it. I wouldn't wish that pain on my worst enemy.
在 PC 环境中进行单元测试(使用 PC C 编译器编译代码并在 PC 单元测试框架中运行代码)可以获得很多好处,但有几个附带条件:
stdint.h
类型,例如uint16_t
而不是普通的unsigned int
等。我们遵循了这些规则,并发现在 PC 单元测试框架中对应用程序层代码进行单元测试后,我们可以非常确信它运行良好。
在PC平台上进行单元测试的优点:
There can be a lot to be gained by unit testing in a PC environment (compiling your code with a PC C compiler and running your code in a PC unit testing framework), with several provisos:
stdint.h
types such asuint16_t
rather than plainunsigned int
etc.We've followed these rules, and found that after unit testing the application-layer code in a PC unit test framework, we can have a good amount of confidence that it works well.
Advantages of unit testing on the PC platform:
嵌入式系统是一个广泛的话题,但总的来说,我们将其视为结合了硬件和软件的特定用途产品。 我的嵌入式背景来自手机,它只是所有嵌入式系统的一小部分。 我将尝试将以下几点保留在抽象方面:
尽可能抽象出硬件依赖关系。 通过这种方式,您可以在模拟的“硬件”上运行单元测试,还可以测试各种罕见/例外的情况,这些情况很难在目标上进行测试。 为了防止抽象成本,您可以使用条件编译等。
尽可能少地依赖硬件。
在模拟器或交叉编译器环境上运行的单元测试仍然不能保证代码在目标硬件上运行。 您还必须针对目标进行测试。 尽早在目标上进行测试。
Embedded systems is a wide topic but in general, let's think of it as a specific-purpose product that combines both hardware and software. My embedded background is from mobile phones which is just a small subset of all embedded systems. I'll try to keep the following points a bit on the abstract side:
Abstract out hardware dependencies whenever possible. This way you can run your unit tests on mocked "hardware" and also test various rare/exceptional cases that would be harder to test on target. To prevent abstraction costs, you can use e.g. conditional compilation.
Have as little as possible depend on the hardware.
Unit tests running on an emulator or cross-compiler environment still does not guarantee the code works on target hardware. You must test on target as well. Test on target as early as possible.
您可能想查看测试驱动开发James W. Grenning 的嵌入式 C。 该书计划于 2010 年 8 月出版,但测试版书现已在 实用书架。
You might want to check out Test Driven Development for Embedded C by James W. Grenning. The book is scheduled to be published in August 2010, but the beta book is available now on The Pragmatic Bookshelf.
这里有缺乏经验的声音,但这也是我最近一直在思考的事情。 在我看来,最好的方法是
A) 在 PC 环境中编写尽可能多的独立于硬件的应用程序代码,然后再将其编写到目标上,并同时编写单元测试(执行首先在 PC 上这应该有助于迫使您分离与硬件无关的东西)。 这样您就可以使用您选择的单元测试仪,然后以老式方式测试依赖于硬件的东西 - 使用 RS-232 和/或示波器和 I/O 引脚发送与时间相关的数据信号,具体取决于它的运行速度。
B) 将其全部写入目标硬件上,但有一个 make 目标来有条件地编译单元测试版本,该版本将运行单元测试并通过 RS-232 或其他方式输出结果(或可分析结果的数据)。 如果你没有足够的内存,这可能会很棘手。
编辑 2009 年 7 月 3 日
我只是对如何对依赖于硬件的东西进行单元测试有另一个想法。 如果您的硬件事件发生得太快而无法使用 RS-232 进行记录,但您又不想手动筛选大量示波器数据检查来查看 I/O 引脚标志是否按预期上升和下降,您可以使用 PC具有集成 DIO 的卡(例如 National Instruments 的数据采集卡系列)可自动评估这些信号的时序。 然后,您只需在 PC 上编写软件来控制数据采集卡与当前运行的单元测试同步。
Voice of inexperience here, but this is something I've been thinking about as well lately. It seems to me that the best approach would be either
A) Write as much of your hardware-independent application code as you can in a PC environment, before you write it on the target, and write your unit tests at the same time (doing it this on the PC first should help force you to separate the hardware-independent stuff). This way you can use your choice of unit testers, then test the hardware-dependent stuff the old fashioned way - with RS-232 and/or oscilloscopes and I/O pins signalling time-dependent data, depending on how fast it has to run.
B) Write it all on the target hardware, but have a make target to conditionally compile a unit test build that will run unit tests and output the results (or data that can be analyzed for results) via RS-232 or some other means. If you don't have a lot of memory, this can be tricky.
Edit 7/3/2009
I just had another thought about how to unit test hardware dependent stuff. If your hardware events are happening too fast to record with RS-232, but you don't want to manually sift through tons of oscilloscope data checking to see if your I/O pin flags rise and fall as expected, you can use a PC card with integrated DIO (such as National Instruments' line of Data Acquisition cards) to automatically evaluate the timing of those signals. You would then just need to write the software on your PC to control the data acquisition card to synchronize with the currently running unit test.
我们设法使用模拟器测试了相当多的硬件相关代码,我们使用 Keil 的模拟器和 IDE(不附属,只是使用他们的工具)。 我们编写模拟器脚本来以我们期望的反应方式驱动“硬件”,并且我们能够非常可靠地测试我们的工作代码。 当然,为某些测试建立硬件模型可能需要一些努力,但对于大多数事情来说,这非常有效,并且允许我们在没有任何可用硬件的情况下完成很多工作。 在访问硬件之前,我们已经能够在模拟器中获得接近完整的系统工作,并且一旦将代码放到真实的东西上,就几乎没有什么问题需要处理。 这还可以显着加快代码的生成速度,因为在模拟芯片时,所有事情都可以在 PC 上完成,并使用更深入的调试器来完成,而不是尝试在硬件上完成所有事情。
使其能够在复杂的控制系统、内存接口、定制 SPI 驱动 IC 甚至单色显示器中可靠地工作。
We manage to get quite a bit of hardware dependent code tested using a simulator, we use Keil's simulator and IDE (not affiliated just use their tools). We write the simulator scripts to drive the 'hardware' in a way we expect it to react and we are able to pretty reliably test our working code. Granted it can take some effort to model the hardware for some tests, but for most things this works very well and allows us to get a lot done without any hardware available. We have been able to get near complete system working in the simulator before having access to hardware and have had very few issues to deal with once putting the code on the real thing. This can also significantly speed up production of code since everything can be done on the PC with the more in-depth debugger available while simulating the chip vs trying to do everything on the hardware.
Have gotten this to work reliably for complex control systems, memory interfaces, custom SPI driven ICs and even a mono-display.
这里有很多很好的答案,一些没有提到的事情是运行诊断代码以便:
Log HAL events (interrupts, bus messages, etc)
Have code to keep track of your resources, (all active semaphores, thread activity)
Have a capture ram mechanism to copy the heap and memory content to persistent storage (hard disk or equivalent) to detect and debug deadlocks, livelocks, memory leaks, buffer overflows, etc.
There's lots of good answers here, some things that haven't been mentioned is to have diagnostic code running in order to:
Log HAL events (interrupts, bus messages, etc)
Have code to keep track of your resources, (all active semaphores, thread activity)
Have a capture ram mechanism to copy the heap and memory content to persistent storage (hard disk or equivalent) to detect and debug deadlocks, livelocks, memory leaks, buffer overflows, etc.
当我去年面临这个问题时,我真的很想在嵌入式平台本身上进行测试。 我正在开发一个库,并使用 RTOS 调用和嵌入式平台的其他功能。 没有任何具体可用的内容,因此我根据我的目的调整了 UnitTest++ 代码。 我在 NetBurner 系列上进行编程,由于它有一个嵌入式 Web 服务器,因此编写基于 Web 的程序非常简单GUI 测试运行器提供经典的红色/绿色反馈。 结果非常好,现在单位测试变得更加容易,并且我对代码在实际硬件上的工作原理感到更加自信。 我什至使用单元测试框架来进行集成测试。 首先,我模拟/存根硬件并注入该接口进行测试。 但最终我编写了一些人机循环测试来测试实际的硬件。 事实证明,这是一种更简单的了解硬件的方法,并且有一种简单的方法可以从嵌入式陷阱中恢复。 由于测试都是从 AJAX 回调到 Web 服务器运行,因此只有手动调用测试才会发生陷阱,并且系统总是在陷阱发生后几秒钟干净地重新启动。
NetBurner 的速度足够快,写入/编译/下载/运行测试周期约为 30 秒。
When I was facing this last year I really wanted to test on the embedded platform itself. I was developing a library and I was using the RTOS calls and other features of the embedded platform. There wasn't anything specific available so I adapted the UnitTest++ code to my purposes. I program on the NetBurner family and since it has an embedded web server, it was pretty straight forward to write a web based GUI test runner that give the classic RED/GREEN feedback. It turned out pretty well, and now unit testing is much easier and I feel much more confident knowing the code works on the actual hardware. I even use the unit testing framework to do integration tests. At first I mocks/stub the hardware and inject that interface to test. But eventually I write some man-in-the-loop tests that exercise the actual hardware. It turns out to be a much simpler way to learn about the hardware and have an easy way to recover from embedded traps. Since the tests all run from AJAX callbacks to the web server a trap only happens as the result of manually invoking a test and the system always restarts cleanly a few seconds after the trap.
The NetBurner is fast enough that the write/compile/download/run test cycle is about 30 seconds.
评估板上有许多嵌入式处理器,因此尽管您可能没有真正的 I/O 设备,但通常您可以在其中一种设备上执行大量算法和逻辑,通常可以通过以下方式进行硬件调试: j标签。 无论如何,“单元”测试通常更多的是关于你的逻辑而不是你的 I/O。 问题通常是从这些环境之一中取出测试工件。
Lots of embedded processors are available on eval boards, so although you may not have your real i/o devices, often you can execute a good deal of your algorithms and logic on one of these kinds of things, often w/hardware debugging available via jtag. And 'unit' tests usually are more about your logic than your i/o anyway. Problem is usually getting your test artifacts back out of one of these environments.
将代码拆分为与设备相关的代码和与设备相关的代码 与设备无关。 独立的代码可以进行单元测试,而不需要太多的痛苦。 只需要手动测试相关代码,直到您拥有流畅的通信接口。
如果您正在编写通信接口,我很抱歉。
Split the code between device-dependent & device-independent. The independent code can be unit-tested without too much pain. The dependent code will simply need to be hand-tested until you have a smooth communications interface.
If you're writing the communications interface, I'm sorry.