如何对 Arduino 代码进行单元测试?
我希望能够对我的 Arduino 代码进行单元测试。理想情况下,我能够运行任何测试,而无需将代码上传到 Arduino。有哪些工具或库可以帮助我解决这个问题?
有一个 Arduino 模拟器正在开发,它可能很有用,但似乎还没有即可使用。
Atmel 的 AVR Studio 包含一个可能有用的芯片模拟器,但我不知道如何将它与 Arduino IDE 结合使用。
I'd like to be able to unit test my Arduino code. Ideally, I would be able to run any tests without having to upload the code to the Arduino. What tools or libraries can help me with this?
There is an Arduino emulator in development which could be useful, but it doesn't yet seem to be ready for use.
AVR Studio from Atmel contains a chip simulator which could be useful, but I can't see how I would use it in conjunction with the Arduino IDE.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(21)
simavr 是一个使用 avr-gcc 的 AVR 模拟器。
它已经支持一些 ATTiny 和 ATMega 微控制器,而且据作者称,添加更多微控制器很容易。
示例中包含 simduino,一个 Arduino 模拟器。它支持运行 Arduino 引导加载程序,并且可以通过 Socat (修改后的 < a href="https://en.wikipedia.org/wiki/Netcat" rel="nofollow noreferrer">Netcat)。
simavr is an AVR simulator using avr-gcc.
It already supports a few ATTiny and ATMega microcontrollers, and - according to the author - it's easy to add some more.
In the examples lies simduino, an Arduino emulator. It supports running the Arduino bootloader and can be programmed with avrdude through Socat (a modified Netcat).
您可以使用我的项目 PySimAVR 在 Python 中进行单元测试。 Arscons 用于构建,simavr 用于模拟。
示例:
开始测试:
You can unit test in Python with my project, PySimAVR. Arscons is used for building and simavr for simulation.
Example:
Start test:
为此,我构建了
arduino_ci
。尽管它仅限于测试 Arduino 库(而不是独立的草图),但它允许在本地或 CI 系统(如 Travis CI 或 Appveyor)上运行单元测试。考虑 Arduino 库目录中的一个非常简单的库,名为
DoSomething
,带有do-something.cpp
:您可以按如下方式对其进行单元测试(使用名为 < code>test/is_four.cpp 或类似的):
仅此而已。如果
assertEqual
语法和测试结构看起来很熟悉,那是因为我采用了一些 Matthew Murdoch 的 ArduinoUnit图书馆他在他的回答中提到了这一点。
有关单元测试的更多信息,请参阅 Reference.md O 引脚、时钟、串行端口等。
这些单元测试使用 ruby gem 中包含的脚本进行编译和运行。有关如何设置的示例,请参阅 README.md 或仅复制以下示例之一:
I built
arduino_ci
for this purpose. Although it's limited to testing Arduino libraries (and not standalone sketches), it enables unit tests to be run either locally or on a CI system (like Travis CI or Appveyor).Consider a very simple library in your Arduino Library directory, called
DoSomething
, withdo-something.cpp
:You'd unit test it as follows (with a test file called
test/is_four.cpp
or some such):That's all. If that
assertEqual
syntax and test structure looks familiar, it's because I adopted some of Matthew Murdoch's ArduinoUnit librarythat he referred to in his answer.
See Reference.md for more information about unit testing I/O pins, the clock, Serial ports, etc.
These unit tests are compiled and run using a script contained in a ruby gem. For examples of how to set that up, see the README.md or just copy from one of these examples:
我不知道有什么平台可以测试 Arduino 代码。
但是,有 Fritzing 平台,您可以使用它对硬件进行建模,然后再进行建模导出PCB图之类的东西。
值得检查。
I am not aware of any platform which can test Arduino code.
However, there is the Fritzing platform, which you can use to model the hardware and later on export PCB diagrams and stuff.
Worth checking.
我们在大型科学实验中使用 Arduino 板进行数据采集。随后,我们必须支持多个具有不同实现的 Arduino 板。我编写了 Python 实用程序来在单元测试期间动态加载 Arduino 十六进制图像。下面链接中的代码通过配置文件支持 Windows 和 Mac OS X。要了解 Arduino IDE 放置十六进制图像的位置,请在按下构建(播放)按钮之前按下 Shift 键。按下 Shift 键并点击上传即可查明您的 avrdude(命令行上传实用程序)在您的系统/Arduino 版本上的位置。或者,您可以查看包含的配置文件并使用您的安装位置(当前在 Arduino 0020 上)。
http://github.com/toddstavish/Python-Arduino-Unit-Testing
We are using Arduino boards for data acquisition in a large scientific experiment. Subsequently, we have to support several Arduino boards with different implementations. I wrote Python utilities to dynamically load Arduino hex images during unit testing. The code found on the link below supports Windows and Mac OS X via a configuration file. To find out where your hex images are placed by the Arduino IDE, hit the shift key before you hit the build (play) button. Hit the shift key while hitting upload to find out where your avrdude (command line upload utility) is located on your system / version of Arduino. Alternatively, you can look at the included configuration files and use your install location (currently on Arduino 0020).
http://github.com/toddstavish/Python-Arduino-Unit-Testing
该程序允许自动运行多个 Arduino 单元测试。测试过程在 PC 上启动,但测试在实际的 Arduino 硬件上运行。一组单元测试通常用于测试一个 Arduino 库。
(这个
Arduino论坛:http://arduino.cc/forum/index.php?topic= 140027.0
GitHub 项目页面:http://jeroendoggen.github.com/Arduino-TestSuite
Python 包索引中的页面: http://pypi.python.org/pypi/arduino_testsuite
单元测试是使用“Arduino 单元测试库”编写的:http://code.google.com/p /arduinounit
对每组单元测试执行以下步骤:
This program allows automated running of several Arduino unit tests. The testing process is started on the PC but the tests run on the actual Arduino hardware. One set of unit tests is typically used to test one Arduino library.
(this
Arduino Forum: http://arduino.cc/forum/index.php?topic=140027.0
GitHub project page: http://jeroendoggen.github.com/Arduino-TestSuite
Page in the Python Package Index: http://pypi.python.org/pypi/arduino_testsuite
The unit tests are written with the "Arduino Unit Testing Library": http://code.google.com/p/arduinounit
The following steps are performed for each set of unit tests:
将特定于硬件的代码与其余代码分开或抽象出来,以便您可以在拥有良好工具且最熟悉的任何平台上测试和调试更大的“其余代码”。
基本上,尝试从尽可能多的已知可用的构建块中构建尽可能多的最终代码。剩下的特定于硬件的工作将变得更加容易和更快。您可以自己使用现有的模拟器和/或模拟设备来完成它。然后,当然,您需要以某种方式测试真实的东西。根据具体情况,这可能会或可能不会很好地自动化(即谁或什么将按下按钮并提供其他输入?谁或什么将观察和解释各种指示器和输出?)。
Keep hardware-specific code separate or abstracted away from the rest so you can test and debug that bigger "rest" on any platform for which you have good tools and with which you're familiar most.
Basically, try to build as much of the final code from as many known-to-work building blocks as possible. The remaining hardware-specific work will then be much easier and faster. You may finish it by using existing emulators and/or emulating devices on your own. And then, of course, you'll need to test the real thing somehow. Depending on circumstances, that may or may not be very well automatable (i.e. who or what will press buttons and provide other inputs? who or what will observe and interpret various indicators and outputs?).
James W. Grenning 写了很棒的书,这本书是关于嵌入式 C 代码的单元测试嵌入式 C 的测试驱动开发。
James W. Grenning writes great books and this one is about unit testing embedded C code Test Driven Development for Embedded C.
我在编写 Arduino 代码时使用 Searduino 。 Searduino 是一个 Arduino 模拟器和一个开发环境(Makefile、C 代码...),使您可以使用您最喜欢的编辑器轻松破解 C/C++。您可以导入 Arduino 草图并在模拟器中运行它们。
Searduino 0.8 的屏幕截图: http://searduino.files.wordpress .com/2014/01/jearduino-0-8.png
Searduino 0.9 将在最后的测试完成后发布,并录制视频......在一两天内。
在模拟器上进行的测试不应被视为真正的测试,但它确实帮助我发现了愚蠢/逻辑错误(忘记执行
pinMode(xx, OUTPUT)
等)。顺便说一句:我是 Searduino 的开发者之一。
I am using Searduino when writing Arduino code. Searduino is an Arduino simulator and a development environment (Makefiles, C code ...) that makes it easy to hack in C/C++ using your favorite editor. You can import Arduino sketches and run them in the simulator.
Screenshot of Searduino 0.8: http://searduino.files.wordpress.com/2014/01/jearduino-0-8.png
Searduino 0.9 will be released and a video will be recorded as soon as the lasts tests are done .... in a day or two.
Testing on the simulator is not to be considered as real tests, but it certainly have helped me a lot in finding stupid/logical mistakes (forgetting to do
pinMode(xx, OUTPUT)
, etc.).BTW: I am one of the people developing Searduino.
有一个名为 ncore 的项目,它为 Arduino 提供原生核心。并允许您为 Arduino 代码编写测试。
从项目描述来看
另外在 “我需要什么来使用它”部分
There is a project called ncore, which provides native core for Arduino. And allows you to write tests for Arduino code.
From the project description
Also on the "what do I need to use it" section
如果您想在 MCU 之外(在桌面上)对代码进行单元测试,请查看 libcheck:
https://libcheck.github.io/check/
我用它来测试我自己的嵌入式代码几次。这是非常强大的框架。
If you want to unit-test code outside MCU (on desktop), check out libcheck:
https://libcheck.github.io/check/
I used it to test my own embedded code few times. It's pretty robust framework.
您可以使用 emulare — 您可以将微控制器拖放到图表上并在 Eclipse 中运行代码。网站上的文档告诉您如何设置。
You can use emulare — you can drag and drop a microcontroller on a diagram and run your code in Eclipse. The documentation on the website tells you how to set it up.
将 Proteus VSM 与 Arduino 库结合使用来调试或测试代码。
在将代码加载到板上之前,这是最佳实践,但请确保计时,因为仿真不会像在板上运行时那样实时运行。
Use Proteus VSM with an Arduino library to debug your code or to test it.
It is a best practice before getting your code onboard, but be sure with timings because the simulation does not run realtime as they run on the board.
尝试Autodesk 电路模拟器。它允许使用许多其他硬件组件测试 Arduino 代码和电路。
Try Autodesk circuit simulator. It allows to test Arduino code and circuits with many other hardware components.
Arduino 基本是用 C 和 C++ 编写的,甚至 arduino 的库也是用 C 和 C++ 编写的。因此,简单来说,只需将代码处理为 C 和 C++ 并尝试进行单元测试。在这里,通过“句柄”这个词,我的意思是您将所有基本语法(例如serial.println更改为sysout,pinmode更改为variables,void循环更改为while()循环)等基本语法,这些语法在keystock中或在某些迭代后会中断。
我知道这是一个漫长的过程,而且不是那么简单。根据我个人的经验,一旦你开始使用它,这就会变得更可靠。
-Nandha_Frost
In basic Arduino is written with C and C++, even libraries of arduino are written in C and C++. So,in simple terms just handle the code as C and C++ and try doing the unit testing. Here, by the word "handle" I mean you to change all the basic syntax like serial.println to sysout, pinmode to varaibles, void loop to while() loop which breaks either in keystock or after some iteration.
I know this is little a long process and not so straight forward.On my personal experience, once you get to do with it, this turns to be more reliable.
-Nandha_Frost
如果您有兴趣运行 INO 草图并检查串行输出,我在我的 Arduino NMEA 校验和 项目。
以下脚本获取该文件并使用 Arduino CLI 将其编译为 HEX 文件,然后将该文件加载到 SimAVR,SimAVR 对其进行评估并打印串行输出。由于所有 Arduino 程序都会永远运行,而没有真正选择杀死自己(
exit(0)
不起作用),因此我让草图运行几秒钟,然后将捕获的输出与预期输出进行比较。下载并解压 Arduino CLI(在本例中为版本 0.5.0 - 撰写本文时的最新版本):
现在您可以更新索引并安装适当的核心:
假设您的草图名为
nmea-checksum.ino
,要获取 ELF 和 HEX,请运行:下一步,SimAVR 运行 HEX(或 ELF) - 我从源代码构建,因为最新版本不适合我:
成功编译将为您提供
simavr/run_avr< /code> 您可以使用它来运行草图。就像我说的,
超时
否则它永远不会终止:生成的文件将有 ANSI 颜色代码控制字符包装串行输出,以摆脱这些:
现在您需要做的就是比较此文件到已知良好的文件:
如果没有差异,
diff
将以代码 0 退出,否则脚本将失败。In case you are interested in running an INO sketch and checkout the serial output, I have a working implementation of that in my Arduino NMEA checksum project.
The following script takes the file and uses Arduino CLI to compile it to a HEX file which is then loaded to SimAVR which evaluates it and prints the serial output. Since all Arduino programs run forever without really having an option of killing themselves (
exit(0)
doesn't work), I let the sketch run for a few seconds and then diff the captured output with expected output.Download and extract Arduino CLI (in this case version 0.5.0 - latest at the time of writing):
Now you can update the index and install the appropriate core:
Assuming your sketch is named
nmea-checksum.ino
, to get ELF and HEX, run:Next up, SimAVR to run the HEX (or ELF) - I build from source because the latest release didn't work for me:
Successful compilation will give you
simavr/run_avr
which you can use to run the sketch. Like I said,timeout
it otherwise it will never terminate:The generated file will have ANSI color code control characters wrapping the serial output, to get rid of those:
Now all you need to do is compared this file to a known good file:
If there are no differences,
diff
will exit with code 0, otherwise the script will fail.很多有趣的答案。我发现 AUnit 似乎受到支持并允许通过 < a href="https://github.com/bxparks/EpoxyDuino" rel="nofollow noreferrer">EpoxyDuino。
我可以想象使用模拟器编写单元测试(由于缺乏仅在本机运行的选项)并在实际硬件上进行集成测试。
A lot of interesting answers. I see though that AUnit seems to be supported and allows local testing via EpoxyDuino.
I can imagine writing unit tests using emulator (for the lack of an option to run just natively) and integration tests on the actual hardware.
不要在 Arduino 设备或仿真器上运行单元测试
针对微控制器设备/仿真器/Sim 的测试的案例
单元测试的目的是测试自己代码的质量。单元测试通常不应该测试您无法控制的因素的功能。
这样想:即使您要测试 Arduino 库、微控制器硬件或仿真器的功能,此类测试结果绝对不可能告诉您有关您的产品质量的任何信息。自己的工作。因此,编写不在目标设备(或模拟器)上运行的单元测试更有价值、更高效。
对目标硬件进行频繁测试的周期极其缓慢:
步骤 3 特别令人讨厌通过串行端口获取诊断消息,但您的项目本身需要使用 Arduino 唯一的硬件串行端口。如果您认为 SoftwareSerial 库可能有所帮助,您应该知道这样做可能会破坏任何需要精确定时的功能,例如同时生成其他信号。这个问题发生在我身上。
再说一次,如果您要使用模拟器测试您的草图,并且您的时间关键型例程在上传到实际的 Arduino 之前都运行完美,那么您将学到的唯一教训是模拟器有缺陷 - 并且仍然知道这一点没有透露任何关于您自己的工作质量的信息。
如果在设备或模拟器上进行测试很愚蠢,我应该做什么?
您可能正在使用计算机来处理 Arduino 项目。该计算机比微控制器快几个数量级。编写测试以在您的计算机上构建和运行。
请记住,Arduino 库和微控制器的行为应该假设是正确的,或者至少始终不正确。
当您的测试产生的输出与您的预期相反时,那么您所测试的代码中可能存在缺陷。如果您的测试输出符合您的预期,但当您将其上传到 Arduino 时程序无法正常运行,那么您就知道您的测试是基于不正确的假设,并且您的测试可能存在缺陷。无论哪种情况,您都将获得关于下一个代码更改应该是什么的真正见解。您的反馈质量从“某些内容损坏”改进为“此特定代码损坏”。
如何在您的 PC 上构建和运行测试
您需要做的第一件事是确定您的测试目标。考虑一下您想要测试您自己的代码的哪些部分,然后确保以可以隔离离散部分的方式构建您的程序以进行测试。
如果您想要测试的部件调用任何 Arduino 函数,您将需要在测试程序中提供模型替换。这比看起来要少得多。您的模型实际上不需要做任何事情,只需为您的测试提供可预测的输入和输出。
您想要测试的任何您自己的代码都需要存在于 .pde 草图之外的源文件中。不用担心,即使在草图之外有一些源代码,您的草图仍然可以编译。当您真正认真对待它时,只需在草图文件中定义程序的正常入口点即可。
剩下的就是编写实际的测试,然后使用您最喜欢的 C++ 编译器进行编译!现实世界的例子可能最好地说明这一点。
实际工作示例
此处发现我的一个宠物项目有一些在 PC 上运行的简单测试。对于这个答案提交,我将回顾一下我如何模拟一些 Arduino 库函数以及我为测试这些模型而编写的测试。这与我之前所说的不测试其他人的代码并不矛盾,因为我是编写模型的人。我想非常确定我的模型是正确的。
mock_arduino.cpp 的源代码,其中包含复制 Arduino 库提供的一些支持功能的代码:
当我的代码将二进制数据写入硬件串行设备时,我使用以下模型来生成可读输出。
fake_serial.h
fake_serial.cpp
最后是实际的测试程序:
这篇文章足够长,所以请参考 我的项目GitHub 查看更多正在运行的测试用例。我将正在进行的工作保存在 master 以外的分支中,因此也请检查这些分支以进行额外的测试。
我选择编写自己的轻量级测试例程,但也可以使用更强大的单元测试框架,例如 CppUnit。
Don't Run Unit Tests on the Arduino Device or Emulator
The case against microcontroller Device/Emulator/Sim-based tests
The purpose of unit testing is to test the quality of your own code. Unit tests should generally never test the functionality of factors outside of your control.
Think about it this way: Even if you were to test functionality of the Arduino library, the microcontroller hardware, or an emulator, it is absolutely impossible for such test results to tell you anything about the quality of your own work. Hence, it is far more valuable and efficient to write unit tests that do not run on the target device (or emulator).
Frequent testing on your target hardware has a painfully slow cycle:
Step 3 is particularly nasty if you expect to get diagnostic messages via serial port but your project itself needs to use your Arduino's only hardware serial port. If you were thinking that the SoftwareSerial library might help, you should know that doing so is likely to disrupt any functionality that requires accurate timing like generating other signals at the same time. This problem has happened to me.
Again, if you were to test your sketch using an emulator and your time-critical routines ran perfectly until you uploaded to the actual Arduino, then the only lesson you're going to learn is that the emulator is flawed--and knowing this still reveals nothing about the quality of your own work.
If it's silly to test on the device or emulator, what should I do?
You're probably using a computer to work on your Arduino project. That computer is orders of magnitudes faster than the microcontroller. Write the tests to build and run on your computer.
Remember, the behavior of the Arduino library and microcontroller should be assumed to be either correct or at least consistently incorrect.
When your tests produce output contrary to your expectations, then you likely have a flaw in your code that was tested. If your test output matches your expectations, but the program does not behave correctly when you upload it to the Arduino, then you know that your tests were based on incorrect assumptions and you likely have a flawed test. In either case, you will have been given real insights on what your next code changes should be. The quality of your feedback is improved from "something is broken" to "this specific code is broken".
How to Build and Run Tests on Your PC
The first thing you need to do is identify your testing goals. Think about what parts of your own code you want to test and then make sure to construct your program in such a way that you can isolate discrete parts for testing.
If the parts that you want to test call any Arduino functions, you will need to provide mock-up replacements in your test program. This is much less work than it seems. Your mock-ups don't have to actually do anything but providing predictable input and output for your tests.
Any of your own code that you intend to test needs to exist in source files other than the .pde sketch. Don't worry, your sketch will still compile even with some source code outside of the sketch. When you really get down to it, little more than your program's normal entry point should be defined in the sketch file.
All that remains is to write the actual tests and then compile it using your favorite C++ compiler! This is probably best illustrated with a real world example.
An actual working example
One of my pet projects found here has some simple tests that run on the PC. For this answer submission, I'll just go over how I mocked-up some of Arduino library functions and the tests I wrote to test those mock-ups. This is not contrary to what I said before about not testing other people's code because I was the one who wrote the mock-ups. I wanted to be very certain that my mock-ups were correct.
Source of mock_arduino.cpp, which contains code that duplicates some support functionality provided by the Arduino library:
I use the following mock-up to produce readable output when my code writes binary data to the hardware serial device.
fake_serial.h
fake_serial.cpp
and finally, the actual test program:
This post is long enough, so please refer to my project on GitHub to see some more test cases in action. I keep my works-in-progress in branches other than master, so check those branches for extra tests, too.
I chose to write my own lightweight test routines, but more robust unit-test frameworks like CppUnit are also available.
由于 Arduino 没有任何预先存在的单元测试框架,我创建了 ArduinoUnit。这是一个简单的 Arduino 草图,展示了其用途:
In the absence of any pre-existing unit test frameworks for Arduino, I have created ArduinoUnit. Here's a simple Arduino sketch demonstrating its use:
通过抽象出硬件访问并在测试中模拟它,我在单元测试我的 PIC 代码方面取得了相当大的成功。
例如,我使用抽象 PORTA
那么 SetPortA 可以很容易地被模拟,而无需在 PIC 版本中添加开销代码。
一旦硬件抽象经过一段时间的测试,我很快就会发现,通常代码从测试设备转移到 PIC 并且第一次就能工作。
更新:
我使用#include接缝作为单元代码,#include C++文件中的单元代码作为测试设备,并使用C文件作为目标代码。
作为一个例子,我想多路复用四个 7 段显示器,一个端口驱动段,第二个端口选择显示器。显示代码通过
SetSegmentData(char)
和SetDisplay(char)
与显示器交互。我可以在我的 C++ 测试装置中模拟这些并检查是否获得了我期望的数据。对于目标,我使用#define
,这样我就可以直接赋值,而无需函数调用的开销I have considerable success unit testing my PIC code by abstracting out the hardware access and mocking it in my tests.
For example, I abstract PORTA with
Then SetPortA can easily be mocked, without adding overhead code in the PIC version.
Once the hardware abstraction has been tested a while I soon find that generally code goes from the test rig to the PIC and works first time.
Update:
I use a #include seam for the unit code, #including the unit code in a C++ file for the test rig, and a C file for the target code.
As an example I want to multiplex four 7 segment displays, one port driving the segments and a second selecting the display. The display code interfaces with the displays via
SetSegmentData(char)
andSetDisplay(char)
. I can mock these in my C++ test rig and check that I get the data I expect. For the target I use#define
so that I get a direct assignment without the overhead of a function call看来 emulino 可以完美地完成这项工作。
GitHub 存储库
It seems that emulino would do the job perfectly.
GitHub repository