Mat*_*och 167 embedded unit-testing avr arduino avr-gcc
我希望能够对我的Arduino代码进行单元测试.理想情况下,我可以运行任何测试而无需将代码上传到Arduino.哪些工具或库可以帮助我解决这个问题?
正在开发的Arduino仿真器可能很有用,但它似乎还没有准备好使用.
来自Atmel的AVR Studio包含一个可能有用的芯片模拟器,但我看不出如何将它与Arduino IDE结合使用.
Iro*_*ior 123
有很多关于单元测试意味着什么的讨论,我并不是真的想在这里讨论.这篇文章并未 告诉您避免对最终目标硬件进行所有实际测试.我试图通过从您最平凡和频繁的测试中消除目标硬件来优化您的开发反馈周期.假设被测单位比整个项目小得多.
单元测试的目的是测试您自己的代码的质量.单元测试通常不应测试控件之外的因素的功能.
以这种方式思考:即使您要测试Arduino库,微控制器硬件或仿真器的功能,这些测试结果也绝对不可能告诉您有关您自己工作质量的任何信息.因此,编写不在目标设备(或仿真器)上运行的单元测试更有价值和高效.
对目标硬件进行频繁测试的周期非常缓慢:
如果您希望通过串行端口获取诊断消息,但步骤3特别令人讨厌,但您的项目本身需要使用Arduino唯一的硬件串行端口.如果您认为SoftwareSerial库可能有所帮助,您应该知道这样做可能会破坏任何需要精确计时的功能,例如同时生成其他信号.这个问题发生在我身上.
同样,如果你要使用模拟器测试你的草图,并且你的时间关键例程运行得很好,直到你上传到实际的Arduino,那么你将要学习的唯一教训就是模拟器存在缺陷 - 并且知道这仍然存在没有透露你自己工作的质量.
您可能正在使用计算机来处理Arduino项目.该计算机的速度比微控制器快.编写测试以在您的计算机上构建和运行.
请记住,Arduino库和微控制器的行为应该被认为是正确的或至少始终不正确.
如果您的测试产生的输出与您的预期相反,那么您的代码中可能存在测试缺陷.如果您的测试输出符合您的预期,但是当您将程序上传到Arduino时程序运行不正常,那么您就知道您的测试是基于错误的假设而且您可能存在缺陷测试.在任何一种情况下,您都将获得有关下一个代码更改应该是什么的真实见解.您的反馈质量从"改良的东西坏了",以"这个特定的代码被打破".
您需要做的第一件事是确定您的测试目标.考虑一下您想要测试自己代码的哪些部分,然后确保以这样一种方式构建程序,即可以隔离用于测试的离散部分.
如果要测试的部件调用任何Arduino函数,则需要在测试程序中提供模拟替换.这比看起来要少得多.除了为测试提供可预测的输入和输出外,您的模型不必实际执行任何操作.
您打算测试的任何自己的代码都需要存在于.pde草图以外的源文件中.不用担心,即使草图之外的某些源代码,您的草图仍会编译.当你真正了解它时,应该在草图文件中定义比程序的正常入口点更多的内容.
剩下的就是编写实际的测试,然后使用您最喜欢的C++编译器进行编译!这可能是用现实世界的例子最好地说明的.
我在这里找到的一个宠物项目有一些在PC上运行的简单测试.对于这个答案提交,我将简要介绍一下我如何模拟一些Arduino库函数以及我为测试这些模型而编写的测试.这与我之前所说的关于不测试其他人的代码的内容并不相反,因为我是编写模型的人.我想确定我的模型是正确的.
mock_arduino.cpp的源代码,其中包含复制Arduino库提供的某些支持功能的代码:
#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);
}
Run Code Online (Sandbox Code Playgroud)
当我的代码将二进制数据写入硬件串行设备时,我使用以下模型生成可读输出.
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;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
最后,实际的测试程序:
#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();
}
Run Code Online (Sandbox Code Playgroud)
这篇文章很长,所以请参考我在GitHub上的项目,看看还有更多的测试用例.我将我的作品保留在除了主人以外的分支中,因此请检查这些分支以进行额外的测试.
我选择编写自己的轻量级测试例程,但也可以使用更强大的单元测试框架,如CppUnit.
Mat*_*och 60
在没有任何预先存在的Arduino单元测试框架的情况下,我创建了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();
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*kes 21
通过抽象出硬件访问并在我的测试中模拟它,我在单元测试PIC代码方面取得了相当大的成功.
例如,我抽象PORTA
#define SetPortA(v) {PORTA = v;}
Run Code Online (Sandbox Code Playgroud)
然后可以轻松地模拟SetPortA,而无需在PIC版本中添加开销代码.
一旦硬件抽象测试了一段时间,我很快发现通常代码从测试装置到PIC并且第一次工作.
更新:
我使用#include接缝作为单元代码,#包括测试装备的C++文件中的单元代码,以及目标代码的C文件.
作为一个例子,我想复用四个7段显示器,一个端口驱动段,另一个端口选择显示器.显示代码通过SetSegmentData(char)和与显示器连接SetDisplay(char).我可以在我的C++测试装备中模拟这些并检查我是否得到了我期望的数据.对于我使用的目标#define,我得到一个直接赋值而没有函数调用的开销
#define SetSegmentData(x) {PORTA = x;}
Run Code Online (Sandbox Code Playgroud)
您可以使用我的项目PySimAVR在Python中进行单元测试.Arscons用于建筑和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'
Run Code Online (Sandbox Code Playgroud)
开始测试:
$ nosetests pysimavr/examples/test_example.py
pysimavr.examples.test_example.test_atmega88 ... ok
Run Code Online (Sandbox Code Playgroud)
我们在大型科学实验中使用Arduino板进行数据采集.随后,我们必须支持几个具有不同实现的Arduino板.我编写了Python实用程序,以在单元测试期间动态加载Arduino十六进制图像.以下链接中的代码通过配置文件支持Windows和Mac OS X. 要找出Arduino IDE放置十六进制图像的位置,请在点击构建(播放)按钮之前按Shift键.在点击上传时按下shift键,找出您的avrdude(命令行上传工具)在您的Arduino系统/版本上的位置.或者,您可以查看包含的配置文件并使用您的安装位置(当前在Arduino 0020上).
http://github.com/toddstavish/Python-Arduino-Unit-Testing
小智 6
该程序允许自动运行多个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 Unit Testing Library"编写:http://code.google.com/p/arduinounit
对每组单元测试执行以下步骤:
保持硬件特定的代码与其他代码分开或抽象,这样您就可以在任何平台上测试和调试更大的"休息",这些平台上有很好的工具并且您最熟悉它.
基本上,尝试尽可能多地从尽可能多的已知工作构建块构建最终代码.然后,剩余的硬件特定工作将变得更加容易和快捷.您可以通过自己使用现有的模拟器和/或模拟设备来完成它.然后,当然,你需要以某种方式测试真实的东西.根据具体情况,可能会或可能不会很好地自动化(即谁按或按什么按钮并提供其他输入?谁或什么会观察和解释各种指标和输出?).
小智 5
我在编写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将会发布,一旦完成持续测试,将会在一两天内录制视频.
模拟器上的测试不应被视为真正的测试,但它确实帮助我找到了愚蠢/逻辑错误(忘记做pinMode(xx, OUTPUT)等等).
顺便说一句:我是开发Searduino的人之一.
我arduino_ci为此目的而建造。尽管它仅限于测试Arduino库(而不是独立的草图),但它使单元测试可以在本地或在CI系统(例如Travis CI或Appveyor)上运行。
考虑一下您的Arduino库目录中一个非常简单的库,名为DoSomething,其内容为do-something.cpp:
#include <Arduino.h>
#include "do-something.h"
int doSomething(void) {
return 4;
};
Run Code Online (Sandbox Code Playgroud)
您可以按以下方式对其进行单元测试(使用称为test/is_four.cpp或此类测试文件):
#include <ArduinoUnitTests.h>
#include "../do-something.h"
unittest(library_does_something)
{
assertEqual(4, doSomething());
}
unittest_main() // this is a macro for main(). just go with it.
Run Code Online (Sandbox Code Playgroud)
就这样。如果该assertEqual语法和测试结构看起来很熟悉,那是因为我采用了
他在回答中提到的Matthew Murdoch的ArduinoUnit库。
有关单元测试I / O引脚,时钟,串行端口等的更多信息,请参见Reference.md。
这些单元测试是使用ruby gem中包含的脚本进行编译和运行的。有关如何进行设置的示例,请参见README.md或仅从以下示例之一进行复制:
| 归档时间: |
|
| 查看次数: |
58873 次 |
| 最近记录: |