Arduino 코드를 단위 테스트 할 수 있기를 원합니다. 이상적으로는 코드를 Arduino에 업로드하지 않고도 테스트를 실행할 수 있습니다. 어떤 도구 나 라이브러리가 도움이 될 수 있습니까?
거기입니다 개발에 아두 이노 에뮬레이터 유용 할 수 있지만, 아직 사용할 준비가 될 것 같지 않습니다.
Atmel의 AVR Studio 에는 유용한 칩 시뮬레이터가 포함되어 있지만 Arduino IDE와 함께 사용하는 방법을 알 수 없습니다.
Arduino 코드를 단위 테스트 할 수 있기를 원합니다. 이상적으로는 코드를 Arduino에 업로드하지 않고도 테스트를 실행할 수 있습니다. 어떤 도구 나 라이브러리가 도움이 될 수 있습니까?
거기입니다 개발에 아두 이노 에뮬레이터 유용 할 수 있지만, 아직 사용할 준비가 될 것 같지 않습니다.
Atmel의 AVR Studio 에는 유용한 칩 시뮬레이터가 포함되어 있지만 Arduino IDE와 함께 사용하는 방법을 알 수 없습니다.
답변:
단위 테스트가 의미하는 바에 대해 많은 토론이 있으며 실제로 그것에 대해 논쟁하려고하지는 않습니다. 이 게시물에서는 궁극적 인 대상 하드웨어에 대한 모든 실제 테스트 를 피하도록 지시하지 않습니다 . 가장 평범하고 빈번한 테스트에서 대상 하드웨어를 제거하여 개발 피드백주기를 최적화하는 데 중점을두고 있습니다. 테스트중인 단위는 전체 프로젝트보다 훨씬 작은 것으로 가정합니다.
단위 테스트의 목적은 코드의 품질을 테스트하는 것입니다. 단위 테스트는 일반적으로 통제 범위 밖에서 요인의 기능을 테스트하지 않아야합니다.
Arduino 라이브러리, 마이크로 컨트롤러 하드웨어 또는 에뮬레이터의 기능을 테스트하더라도 이러한 테스트 결과가 자신의 작업 품질에 대한 정보를 제공하는 것은 불가능 합니다. 따라서 대상 장치 (또는 에뮬레이터)에서 실행되지 않는 단위 테스트를 작성하는 것이 훨씬 더 가치 있고 효율적입니다.
대상 하드웨어에 대한 빈번한 테스트로 인해주기가 매우 느립니다.
직렬 포트를 통해 진단 메시지를받을 것으로 예상되지만 프로젝트 자체에서 Arduino의 유일한 하드웨어 직렬 포트를 사용해야하는 경우 3 단계는 특히 불쾌합니다. SoftwareSerial 라이브러리가 도움이 될 것이라고 생각한 경우, 그렇게하면 다른 신호를 동시에 생성하는 것과 같은 정확한 타이밍이 필요한 기능이 중단 될 수 있음을 알아야합니다. 이 문제는 나에게 일어났다.
다시, 에뮬레이터를 사용하여 스케치를 테스트하고 실제 Arduino에 업로드 할 때까지 시간이 중요한 루틴이 완벽하게 실행 된 경우, 배울 유일한 교훈은 에뮬레이터에 결함이 있다는 것입니다. 자신의 작업 품질에 대해서는 아무 것도 나타내지 않습니다 .
컴퓨터를 사용하여 Arduino 프로젝트에서 작업하고있을 것입니다. 그 컴퓨터는 마이크로 컨트롤러보다 훨씬 빠릅니다. 컴퓨터 에서 빌드하고 실행 하기위한 테스트를 작성하십시오 .
Arduino 라이브러리와 마이크로 컨트롤러의 동작은 정확하거나 적어도 일관되게 부정확 한 것으로 가정 해야합니다 .
테스트 결과가 예상과 달리 출력을 생성하면 테스트 된 코드에 결함이있을 수 있습니다. 테스트 결과가 예상과 일치하지만 Arduino에 업로드 할 때 프로그램이 올바르게 작동하지 않으면 테스트가 잘못된 가정에 기초한 것이며 결함이있는 테스트 일 수 있습니다. 두 경우 모두 다음 코드 변경 내용에 대한 실질적인 통찰력을 얻게됩니다. 피드백의 품질이 " 무슨 문제 가 있음"에서 "이 특정 코드 가 깨졌습니다"로 향상되었습니다 .
가장 먼저해야 할 일은 테스트 목표를 파악하는 것 입니다. 테스트하려는 자체 코드 부분을 생각한 다음 테스트를 위해 별도의 부분 을 분리 할 수있는 방식으로 프로그램을 구성하십시오 .
테스트하려는 부품이 Arduino 기능을 호출하는 경우 테스트 프로그램에서 모형 대체품을 제공해야합니다. 이것은 생각보다 훨씬 적은 작업입니다. 실물 모형은 실제로 테스트를 위해 예측 가능한 입력 및 출력을 제공하는 것 외에는 아무것도 할 필요가 없습니다.
테스트하려는 자체 코드는 .pde 스케치 이외의 소스 파일에 존재해야합니다. 걱정하지 마십시오. 스케치 외부에서 소스 코드를 사용해도 스케치가 컴파일됩니다. 실제로 내려 가면 스케치 파일에서 프로그램의 일반적인 진입 점보다 조금만 정의해야합니다.
남아있는 것은 실제 테스트를 작성하고 좋아하는 C ++ 컴파일러를 사용하여 컴파일하는 것입니다! 이것은 실제 사례를 통해 가장 잘 설명 될 수 있습니다.
여기에있는 애완 동물 프로젝트 중 하나 는 PC에서 실행되는 간단한 테스트가 있습니다. 이 답변 제출을 위해 Arduino 라이브러리 함수 중 일부를 모의 한 방법과 해당 모의를 테스트하기 위해 작성한 테스트를 살펴 보겠습니다. 내가 목업을 쓴 사람이기 때문에 다른 사람들의 코드를 테스트하지 않는다는 것에 대해 이전에 말한 것과 반대되는 것은 아닙니다. 나는 목업의 정확성을 확신하고 싶었다.
Arduino 라이브러리에서 제공하는 일부 지원 기능을 복제하는 코드가 포함 된 mock_arduino.cpp의 소스 :
#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;
마지막으로 실제 테스트 프로그램 :
#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의 프로젝트 를 참조하여 테스트 사례가 더 있는지 확인하십시오. 나는 마스터 이외의 지점에서 진행중인 작업을 유지하므로 추가 테스트를 위해 해당 지점을 확인하십시오.
저만의 가벼운 테스트 루틴을 작성하기로 선택했지만 CppUnit과 같은보다 강력한 단위 테스트 프레임 워크도 사용할 수 있습니다.
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();
}
하드웨어 액세스를 추상화하고 테스트에서 조롱하여 PIC 코드를 테스트하는 데 상당한 성공을 거두었습니다.
예를 들어, 나는 PORTA를
#define SetPortA(v) {PORTA = v;}
그러면 PIC 버전에서 오버 헤드 코드를 추가하지 않고도 SetPortA를 쉽게 조롱 할 수 있습니다.
하드웨어 추상화가 잠시 테스트되면 곧 코드가 테스트 장비에서 PIC로 이동하여 처음으로 작동한다는 것을 곧 알았습니다.
최신 정보:
유닛 코드에 #include 심을 사용하고 # 테스트 리그를위한 C ++ 파일에 유닛 코드를 포함하고 # 타겟 코드를위한 C 파일을 사용합니다.
예를 들어, 4 개의 7 세그먼트 디스플레이, 하나의 포트로 세그먼트를 구동하고 다른 하나는 디스플레이를 선택하여 다중화하고 싶습니다. 통해 디스플레이와 디스플레이 인터페이스 코드 SetSegmentData(char)
와 SetDisplay(char)
. C ++ 테스트 장비에서 이것을 조롱하고 내가 기대하는 데이터를 얻었는지 확인할 수 있습니다. 대상 #define
의 경우 함수 호출의 오버 헤드없이 직접 할당을 얻도록 사용 합니다.
#define SetSegmentData(x) {PORTA = x;}
내 프로젝트 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'
테스트 시작 :
$ nosetests pysimavr/examples/test_example.py
pysimavr.examples.test_example.test_atmega88 ... ok
우리는 대규모 과학 실험에서 데이터 수집을 위해 Arduino 보드를 사용하고 있습니다. 결과적으로 다른 구현을 가진 여러 Arduino 보드를 지원해야합니다. 단위 테스트 중에 Arduino 16 진수 이미지를 동적으로로드하는 Python 유틸리티를 작성했습니다. 아래 링크에있는 코드는 구성 파일을 통해 Windows 및 Mac OS X을 지원합니다. Arduino IDE가 16 진 이미지를 배치하는 위치를 찾으려면 빌드 (재생) 버튼을 누르기 전에 Shift 키를 누르십시오. 업로드를 누르는 동안 Shift 키를 눌러 avrdude (명령 줄 업로드 유틸리티)가 시스템 / 버전의 Arduino에있는 위치를 찾으십시오. 또는 포함 된 구성 파일을보고 설치 위치 (현재 Arduino 0020)를 사용할 수 있습니다.
이 프로그램을 사용하면 여러 Arduino 단위 테스트를 자동으로 실행할 수 있습니다. 테스트 프로세스는 PC에서 시작되지만 테스트는 실제 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) 로 작성됩니다 .
각 단위 테스트 세트에 대해 다음 단계가 수행됩니다.
하드웨어 관련 코드를 다른 코드와 분리하거나 추상화하지 않도록 유지하십시오. 툴이 우수하고 가장 친숙한 플랫폼에서 더 큰 "휴식"을 테스트하고 디버깅 할 수 있습니다.
기본적으로 알려진 많은 작업 블록에서 가능한 많은 최종 코드를 작성하십시오. 나머지 하드웨어 관련 작업은 훨씬 쉽고 빠릅니다. 기존 에뮬레이터 및 / 또는 에뮬레이션 장치를 사용하여 완료 할 수 있습니다. 물론 실제 방법을 테스트해야합니다. 상황에 따라, 자동화가 잘 될 수도 있고 그렇지 않을 수도 있습니다 (즉, 누가 또는 무엇을 눌러 버튼을 누르고 다른 입력을 제공합니까? 누가 또는 무엇을 다양한 표시기 및 출력을 관찰하고 해석합니까?).
James W. Grenning은 훌륭한 책을 썼습니다. 이것은 임베디드 C 코드 테스트 중심 개발에 대한 단위 테스트에 관한 것입니다 .
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)
등).
BTW : 저는 Searduino를 개발하는 사람들 중 하나입니다.
arduino_ci
이 목적으로 만들었 습니다. 독립형 스케치가 아닌 Arduino 라이브러리 테스트로 제한되지만 단위 테스트는 로컬 또는 CI 시스템 (예 : Travis CI 또는 Appveyor)에서 실행할 수 있습니다.
라고, 당신의 아두 이노 라이브러리 디렉토리에 아주 간단한 라이브러리를 고려 DoSomething
하여, do-something.cpp
:
#include <Arduino.h>
#include "do-something.h"
int doSomething(void) {
return 4;
};
다음과 같이 (테스트 파일 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.
그게 다야. 그 assertEqual
구문과 테스트 구조가 친숙해 보인다면, 그가 대답
에서 언급 한 Matthew Murdoch의 ArduinoUnit 라이브러리 중 일부를 채택했기 때문 입니다.
단위 테스트 I / O 핀, 클럭, 직렬 포트 등에 대한 자세한 내용은 Reference.md 를 참조하십시오 .
이 단위 테스트는 루비 젬에 포함 된 스크립트를 사용하여 컴파일 및 실행됩니다. 설정 방법에 대한 예는 README.md를 참조 하거나 다음 예 중 하나에서 복사 하십시오 .
Arduino에 기본 코어를 제공하는 ncore 프로젝트가 있습니다 . Arduino 코드에 대한 테스트를 작성할 수 있습니다.
프로젝트 설명에서
기본 코어를 사용하면 일반적으로 수정하지 않고 PC에서 Arduino 스케치를 컴파일하고 실행할 수 있습니다. 표준 Arduino 함수의 기본 버전과 일반적으로 하드웨어 자체에서 오는 스케치에 입력을 제공하는 명령 행 인터프리터를 제공합니다.
테스트를 빌드하려면 http://cxxtest.tigris.org의 cxxtest가 필요합니다 . NCORE는 cxxtest 3.10.1로 테스트되었습니다.
MCU 외부에서 코드를 테스트하려면 (데스크톱에서) libcheck를 확인하십시오. https://libcheck.github.io/check/
임베디드 코드를 몇 번 테스트하는 데 사용했습니다. 꽤 견고한 프레임 워크입니다.
기본적으로 Arduino는 C 및 C ++로 작성되었으며, arduino 라이브러리도 C 및 C ++로 작성되었습니다. 따라서 간단히 말하면 코드를 C 및 C ++로 처리하고 단위 테스트를 시도하십시오. 여기서 "handle"이라는 단어는 serial.println과 같은 모든 기본 구문을 sysout으로, pinmode를 varaibles로, void 루프를 while () 루프로 키 스톡에서 또는 일부 반복 후에 변경하는 것을 의미합니다.
이 과정은 시간이 오래 걸리지 않으며 너무나도 직설적이지 않다는 것을 알고 있습니다. 개인적인 경험으로, 일단 당신이 그것과 관련이 있다면, 이것은 더 신뢰할 만합니다.
-난다 프로스트
INO 스케치를 실행하고 직렬 출력을 체크 아웃하는 데 관심이있는 경우 Arduino NMEA 체크섬 프로젝트 에서 실제로 구현 했습니다.
다음 스크립트는 파일을 가져 와서 Arduino CLI를 사용하여 파일을 HEX 파일로 컴파일 한 다음 SimAVR로로드하여 평가하고 직렬 출력을 인쇄합니다. 모든 Arduino 프로그램은 실제로 죽일 수있는 옵션이 없어도 영원히 실행되므로 ( exit(0)
작동하지 않음) 스케치를 몇 초 동안 실행 한 다음 캡처 된 출력을 예상 출력과 비교합니다.
Arduino CLI를 다운로드하여 압축을 풉니 다 (이 경우 버전 0.5.0-작성 당시 최신 버전).
curl -L https://github.com/arduino/arduino-cli/releases/download/0.5.0/arduino-cli_0.5.0_Linux_64bit.tar.gz -o arduino-cli.tar.gz
tar -xvzf arduino-cli.tar.gz
이제 색인을 업데이트하고 적절한 코어를 설치할 수 있습니다.
./arduino-cli core update-index
./arduino-cli core install arduino:avr
스케치 이름이이라고 가정하면 nmea-checksum.ino
ELF 및 HEX를 얻으려면 다음을 실행하십시오.
./arduino-cli compile -b arduino:avr:uno nmea-checksum.ino
다음으로 SimAVR은 HEX (또는 ELF)를 실행합니다. 최신 릴리스가 작동하지 않기 때문에 소스에서 빌드합니다.
sudo apt-get update
sudo apt-get install -y build-essential libelf-dev avr-libc gcc-avr freeglut3-dev libncurses5-dev pkg-config
git clone https://github.com/buserror/simavr.git
cd simavr
make
성공적으로 컴파일하면 simavr/run_avr
스케치를 실행하는 데 사용할 수 있습니다. 내가 말했듯이, timeout
그렇지 않으면 종료되지 않습니다.
cd simavr
timeout 10 ./run_avr -m atmega168 -f 16000000 ../../nmea-checksum.ino.arduino.avr.uno.elf &> nmea-checksum.ino.clog || true
생성 된 파일에는 직렬 출력을 래핑하는 ANSI 색상 코드 제어 문자가 포함되어 있습니다.
cat nmea-checksum.ino.clog | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > nmea-checksum.ino.log
cat nmea-checksum.ino.log
이제이 파일을 알려진 정상 파일과 비교하기 만하면됩니다.
diff nmea-checksum.ino.log ../../nmea-checksum.ino.test
차이점이 없으면 diff
코드 0으로 종료하고 그렇지 않으면 스크립트가 실패합니다.