我怎样才能unit testingArduino代码?
我想能够unit testing我的Arduino代码。 理想情况下,我将能够运行任何testing,而不必将代码上传到Arduino。 什么工具或图书馆可以帮助我呢?
在开发中有一个Arduino仿真器可能是有用的,但它似乎还没有准备好使用。
来自Atmel的AVR Studio包含一个可能有用的芯片模拟器,但是我不知道如何将它与Arduino IDE结合使用。
不要在Arduino设备或仿真器上运行unit testing
针对单片机设备/仿真器/基于Sim的testing的情况
unit testing的目的是testing你自己的代码的质量。 unit testing不应该testing你的控制之外的因素的function。
这样想:即使你要testingArduino库,微控制器硬件或者仿真器的function,这样的testing结果也绝对不可能告诉你任何有关你自己工作的质量。 因此,编写在设备(或仿真器)上运行的unit testing绝对没有价值 。
无论您是在考虑在设备上运行testing还是在仿真器上运行testing,您仍然在重复一个残酷的过程:
- 调整你的代码
- 编译并上传到Arduino设备
- 观察行为并猜测它是否正常工作。
- 重复
如果您希望通过串口获取诊断消息,但是您的项目本身需要使用Arduino唯一的硬件串行端口,则步骤3特别讨厌。 如果您认为SoftwareSerial库可能有所帮助,那么您应该知道这样做可能会中断任何需要精确计时的function,例如同时生成其他信号。 这个问题发生在我身上。
再一次,如果你要使用模拟器来testing你的草图,并且你的时间关键的例程完美运行,直到你上传到实际的Arduino,那么你将学习的唯一的一课是模拟器是有缺陷的 – 而且知道这仍然没有透露你自己的工作质量!
如果在设备或模拟器上testing是愚蠢的,我该怎么办?
您可能正在使用计算机来处理您的Arduino项目。 那台电脑的速度比单片机快数千倍 。 编写testing以在您的计算机上构build和运行 。
请记住,Arduino库和微控制器的行为应该被认为是正确的或者至less一致的不正确 。
当您的testing产生与您的期望相反的输出时,那么您的代码中可能存在一个已经过testing的缺陷。 如果你的testing结果与你的期望相符,但是当你把testing结果上传到Arduino上时,程序并不正确,那么你知道你的testing是基于不正确的假设,你可能有一个有缺陷的testing。 无论哪种情况,您都将获得关于您的下一个代码更改内容的真实见解。
如何在PC上构build和运行testing
你需要做的第一件事是确定你的testing目标 。 考虑你想要testing的代码的哪些部分,然后确保以这种方式构build你的程序,以便可以隔离分立的部分进行testing。
如果您要testing的部分调用任何Arduinofunction,则需要在testing程序中提供模拟replace。 这比看起来less得多的工作。 您的模型实际上不需要做任何事情,只是为您的testing提供可预测的input和输出。
您打算testing的任何自己的代码都需要存在于.pde草图以外的源文件中。 不要担心,即使在草图之外有一些源代码,草图仍然会被编译。 当您真正了解它时,应该在草图文件中定义比程序的正常入口点更多的地方。
剩下的就是编写实际的testing,然后用你最喜欢的C ++编译器编译它! 这可能是一个真实世界的例子最好的说明。
一个实际的工作例子
我在这里find的一个宠物项目有一些在PC上运行的简单testing。 对于这个答案的提交,我将回顾一下我如何嘲弄Arduino的一些库函数和我编写的testing这些模型的testing。 这与我之前所说的不testing其他人的代码并不矛盾,因为我是编写模型的人。 我想确定我的模型是正确的。
mock_arduino.cpp的来源,其中包含的代码复制了Arduino库提供的一些支持function:
#include <sys/timeb.h> #include "mock_arduino.h" timeb t_start; unsigned long millis() { timeb t_now; ftime(&t_now); return (t_now.time - t_start.time) * 1000 + (t_now.millitm - t_start.millitm); } void delay( unsigned long ms ) { unsigned long start = millis(); while(millis() - start < ms){} } void initialize_mock_arduino() { ftime(&t_start); }
当我的代码将二进制数据写入硬件串行设备时,我使用以下模型生成可读输出。
fake_serial.h
#include <iostream> class FakeSerial { public: void begin(unsigned long); void end(); size_t write(const unsigned char*, size_t); }; extern FakeSerial Serial;
fake_serial.cpp
#include <cstring> #include <iostream> #include <iomanip> #include "fake_serial.h" void FakeSerial::begin(unsigned long speed) { return; } void FakeSerial::end() { return; } size_t FakeSerial::write( const unsigned char buf[], size_t size ) { using namespace std; ios_base::fmtflags oldFlags = cout.flags(); streamsize oldPrec = cout.precision(); char oldFill = cout.fill(); cout << "Serial::write: "; cout << internal << setfill('0'); for( unsigned int i = 0; i < size; i++ ){ cout << setw(2) << hex << (unsigned int)buf[i] << " "; } cout << endl; cout.flags(oldFlags); cout.precision(oldPrec); cout.fill(oldFill); return size; } FakeSerial Serial;
最后,实际的testing程序:
#include "mock_arduino.h" using namespace std; void millis_test() { unsigned long start = millis(); cout << "millis() test start: " << start << endl; while( millis() - start < 10000 ) { cout << millis() << endl; sleep(1); } unsigned long end = millis(); cout << "End of test - duration: " << end - start << "ms" << endl; } void delay_test() { unsigned long start = millis(); cout << "delay() test start: " << start << endl; while( millis() - start < 10000 ) { cout << millis() << endl; delay(250); } unsigned long end = millis(); cout << "End of test - duration: " << end - start << "ms" << endl; } void run_tests() { millis_test(); delay_test(); } int main(int argc, char **argv){ initialize_mock_arduino(); run_tests(); }
这篇文章已经够长了,所以请参考我在GitHub上的项目来看看更多的testing用例。 除了主人以外,我还在工作中进行工作,所以请检查那些分支以进行额外的testing。
我select编写自己的轻量级testing例程,但也可以使用更强大的unit testing框架,如CppUnit。
在Arduino没有任何预先存在的unit testing框架的情况下,我创build了ArduinoUnit 。 下面是一个简单的Arduino草图,演示了它的使用:
#include <ArduinoUnit.h> // Create test suite TestSuite suite; void setup() { Serial.begin(9600); } // Create a test called 'addition' in the test suite test(addition) { assertEquals(3, 1 + 2); } void loop() { // Run test suite, printing results to the serial port suite.run(); }
我有相当多的成功unit testing我的PIC代码抽象硬件访问和嘲笑我的testing。
例如,我抽象PORTA
#define SetPortA(v) {PORTA = v;}
然后SetPortA可以很容易地被模拟,而不需要在PIC版本中添加开销代码。
一旦硬件抽象经过testing,我很快就会发现,代码从testing平台到PIC首次运行。
更新:
我使用#include seam作为单元代码,#包括testing装置的C ++文件中的单元代码和目标代码的C文件。
作为一个例子,我想复用四个7段显示器,一个端口驱动段和第二个select显示。 显示代码通过SetSegmentData(char)
和SetDisplay(char)
与显示器进行SetSegmentData(char)
。 我可以在我的C ++testing平台上嘲笑这些,并检查我是否能得到我期望的数据。 对于目标我使用#define
这样我就可以直接分配而不需要函数调用的开销
#define SetSegmentData(x) {PORTA = x;}
Emulino似乎完美地完成了这项工作。
Emulino是Greg Hewgill的Arduino平台的模拟器。 ( 来源 )
GitHub仓库
simavr是一个使用avr-gcc的AVR 模拟器 。
它已经支持一些ATTiny和ATMega微控制器,而根据作者的说法,很容易添加更多。
在这个例子中,有一个Arduino仿真器simduino。 它支持运行Arduino引导程序,并可以通过Socat (一个修改后的Netcat )通过avrdude进行编程。
您可以使用我的项目PySimAVR以Python进行unit testing。 Arscons用于build模和simavr模拟。
例:
from pysimavr.sim import ArduinoSim def test_atmega88(): mcu = 'atmega88' snippet = 'Serial.print("hello");' output = ArduinoSim(snippet=snippet, mcu=mcu, timespan=0.01).get_serial() assert output == 'hello'
开始testing:
$ nosetests pysimavr/examples/test_example.py pysimavr.examples.test_example.test_atmega88 ... ok
这个程序允许自动运行几个Arduinounit testing。 testing过程在PC上启动,但是testing在实际的Arduino硬件上运行。 一组unit testing通常用于testing一个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
unit testing是用“Arduinounit testing库”编写的: http : //code.google.com/p/arduinounit
为每组unit testing执行以下步骤:
- 阅读configuration文件以找出要运行的testing
- 该脚本编译并上传包含unit testing代码的Arduino草图。
- unit testing在Arduino板上运行。
- testing结果通过串口打印并通过Python脚本进行分析。
- 该脚本开始下一个testing,对configuration文件中请求的所有testing重复上述步骤。
- 该脚本打印一个摘要,显示完整testing套件中所有失败/通过testing的概览。
我不知道任何可以testingArduino代码的平台。
但是,有Fritzing平台,您可以使用它来模拟硬件,然后导出PCB图表和内容。
值得检查。
我们使用Arduino板进行大型科学实验中的数据采集。 随后,我们必须支持不同实现的几个Arduino板卡。 我编写了Python实用程序,在unit testing期间dynamic加载Arduinohex图像。 以下链接中的代码通过configuration文件支持Windows和Mac OS X. 要找出Arduino IDE放置的hex图像的位置,请在按下构build(播放)button之前按Shift键。 点击shift键,find你的avrdude(命令行上传工具)在系统/版本的Arduino上的位置。 或者,您可以查看包含的configuration文件并使用您的安装位置(目前在Arduino 0020上)。
保持硬件特定的代码与其余部分分离或抽象,以便在任何有良好工具且最熟悉的平台上testing和debugging更大的“rest”。
基本上,尽可能多地从尽可能多的已知工作构build块中构build尽可能多的最终代码。 剩下的硬件特定的工作将变得更加容易和快速。 您可以通过使用现有的仿真器和/或自己模拟设备来完成。 然后,当然,你需要以某种方式testing真实的东西。 根据具体情况,这可能是也可能不是很好的自动化(即谁或什么将button和提供其他的input?谁或什么将观察和解释各种指标和产出?)。
编写Arduino代码时使用了Searduino 。 Searduino是一个Arduino模拟器和一个开发环境(Makefiles,C代码…),可以很容易地使用你最喜欢的编辑器在C / C ++中进行攻击。 您可以导入Arduino草图并在模拟器中运行它们。
Searduino 0.8的屏幕截图: http ://searduino.files.wordpress.com/2014/01/jearduino-0-8.png
Searduino 0.9将会被释放,一旦最后的testing完成,video将被logging下来….在一两天内。
在模拟器上的testing并不被认为是真正的testing,但它确实帮助我find愚蠢/逻辑错误(忘记做pinMode(xx, OUTPUT)
等等)。
顺便说一句:我是开发Searduino的人之一。
James W. Grenning写了很棒的书,这个是关于embedded式C的embedded式C代码testing驱动开发的unit testing。
有一个名为ncore的项目,它为Arduino提供本地核心。 并允许您编写testingArduino代码。
从项目描述
本地核心允许您在PC上编译和运行Arduino草图,一般不做修改。 它提供了标准的Arduinofunction的本地版本,以及一个命令行intestiter给你的草图input,通常来自硬件本身。
另外在“我需要使用它”部分
如果你想build立testing,你需要从http://cxxtest.tigris.org获得; cxxtest。 NCORE已经用cxxtest 3.10.1进行了testing。
你可以使用emulare – 你可以在图上拖放一个微控制器,并在Eclipse中运行你的代码。 网站上的文档告诉你如何设置它。
使用带有Arduino库的Proteus VSM来debugging或testing代码。
在获取代码之前,这是一个最佳实践,但要确保时间安排,因为模拟在板上运行时不会实时运行。
尝试Autodesk电路模拟器 。 它允许用许多其他硬件组件testingArduino代码和电路。
如果你想在单片机外testing代码(在桌面上),检查libcheck: https ://libcheck.github.io/check/
我用它来testing我自己的embedded式代码几次。 这是非常强大的框架。