如何对Arduino代码进行单元测试?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (42)

我希望能够对我的Arduino代码进行单元测试。理想情况下,我可以运行任何测试,而不必将代码上传到Arduino。有什么工具或库可以帮助我呢?

Atmel包含一个可能有用的芯片模拟器,但我无法理解如何将它与Arduino IDE结合使用。

提问于
用户回答回答于

下面是一个简单的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();
}
用户回答回答于

不要在Arduino设备或模拟器上运行单元测试

基于单片机/模拟器/Sim的测试实例

单元测试的目的是测试您自己代码的质量。单元测试应该绝不可能测试超出控制范围的因素的功能。

可以这样想:即使要测试arduino库、微控制器硬件或模拟器的功能,它也是绝对不可能这样的测试结果能告诉你任何关于你自己工作质量的事情。因此,绝对有无值在编写单元测试时,在设备(或模拟器)上运行。

无论是考虑在设备上运行测试还是在模拟器上运行测试,仍然在让自己重复一个极其缓慢的过程:

  1. 调整代码
  2. 编译并上传到Arduino设备
  3. 观察行为猜测关于它是否起作用的问题。
  4. 重复

如果希望通过串口获得诊断消息,但是项目本身需要使用您的Arduino的唯一硬件串行端口,那么第3步就特别麻烦了。如果认为SoftwareSerialLibrary可能会有所帮助,那么应该知道,这样做可能会破坏任何需要精确计时的功能,比如同时生成其他信号。这个问题发生在我身上。

同样,如果要使用仿真器测试草图,并且时间关键例程在上传到实际的arduino之前运行得非常完美

当测试产生与预期相反的输出时,代码中可能会出现一个被测试的缺陷。如果测试输出符合期望,但是当将其上传到Arduino时,程序的行为不正确,那么就知道测试是基于错误的假设,并且您可能有一个错误的测试。在这两种情况下, 都将得到关于下一个代码更改应该是什么的真正的洞察力。

如何在个人电脑上建立和运行测试

如果要测试的部件调用任何Arduino函数,则需要在测试程序中提供模拟替换。这比看起来要少得多。除了为测试提供可预测的输入和输出之外,模拟并不需要实际做任何事情。

要测试的任何自己的代码都需要存在于.pde草图以外的源文件中。不要担心,即使在草图之外使用一些源代码,草图仍然会编译。当真正深入到它时,应该在草图文件中定义程序的正常入口点。

剩下的就是编写实际的测试,然后使用您最喜欢的C++编译器编译它!这可能最好用一个真实世界的例子来说明。

实际工作实例

有一些简单的测试运行在电脑上。对于这个提交的答案,我只想介绍一下我是如何模拟一些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);
}

当我的代码将二进制数据写入硬件串行设备时,我使用下面的模拟来生成可读的输出。

#include <iostream>

class FakeSerial {
public:
  void begin(unsigned long);
  void end();
  size_t write(const unsigned char*, size_t);
};

extern FakeSerial 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;

最后,实际的测试程序:

#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();
}

扫码关注云+社区