私のArduinoコードを単体テストできるようにしたいのですが。理想的には、Arduinoにコードをアップロードしなくても、テストを実行できます。これに役立つツールやライブラリはありますか?
ある開発中のArduinoエミュレータに有用かもしれないが、まだ使用のために準備ができていないようです。
AtmelのAVR Studioには便利なチップシミュレーターが含まれていますが、Arduino IDEと組み合わせて使用する方法がわかりません。
私のArduinoコードを単体テストできるようにしたいのですが。理想的には、Arduinoにコードをアップロードしなくても、テストを実行できます。これに役立つツールやライブラリはありますか?
ある開発中のArduinoエミュレータに有用かもしれないが、まだ使用のために準備ができていないようです。
AtmelのAVR Studioには便利なチップシミュレーターが含まれていますが、Arduino IDEと組み合わせて使用する方法がわかりません。
回答:
単体テストが何を意味するかについては多くの議論があり、私は実際にそれについてここで議論するつもりはありません。この投稿は、最終的なターゲットハードウェアでの実際的なテストをすべて 回避するように指示するものではありません。最も平凡で頻繁なテストからターゲットハードウェアを排除することにより、開発フィードバックサイクルの最適化について説明します。テスト対象のユニットは、プロジェクト全体よりもはるかに小さいと想定されています。
単体テストの目的は、独自のコードの品質をテストすることです。単体テストでは、通常、制御できない要因の機能をテストするべきではありません。
このように考えてください。たとえArduinoライブラリ、マイクロコントローラーハードウェア、またはエミュレーターの機能をテストしたとしても、そのようなテスト結果から自分の作業の品質について何かを伝えることは絶対に不可能です。したがって、ターゲットデバイス(またはエミュレータ)で実行されない単体テストを記述する方がはるかに価値が高く効率的です。
ターゲットハードウェアで頻繁にテストを行うと、サイクルが非常に遅くなります。
ステップ3は、シリアルポートを介して診断メッセージを取得することを期待しているが、プロジェクト自体がArduinoの唯一のハードウェアシリアルポートを使用する必要がある場合に特に厄介です。SoftwareSerialライブラリが役立つと考えている場合は、そうすることで、同時に他の信号を生成するなど、正確なタイミングを必要とする機能が混乱する可能性があることを知っておく必要があります。この問題は私に起こりました。
繰り返しになりますが、エミュレータを使用してスケッチをテストし、実際のArduinoにアップロードするまでタイムクリティカルなルーチンが完全に実行された場合、学ぶべき唯一の教訓は、エミュレータに欠陥があることです。あなた自身の仕事の質について何も明らかにしません。
コンピューターを使用してArduinoプロジェクトで作業している可能性があります。そのコンピューターはマイクロコントローラーよりも桁違いに速い。コンピュータでビルドして実行するテストを記述します。
Arduinoライブラリとマイクロコントローラーの動作は正しいか、少なくとも一貫して正しくないと想定されていることを忘れないでください。
テストが期待に反する出力を生成する場合、テストされたコードに欠陥がある可能性があります。テスト出力が期待どおりであるが、Arduinoにアップロードしたときにプログラムが正しく動作しない場合、テストが誤った仮定に基づいており、テストに欠陥がある可能性があることがわかります。どちらの場合も、次のコード変更がどうあるべきかについての真の洞察が与えられます。フィードバックの質が「何かが壊れている」から「この特定のコードが壊れている」に改善されます。
最初に行う必要があるのは、テストの目標を特定することです。独自のコードのどの部分をテストしたいかを考え、テストのために個別の部分を分離できるような方法でプログラムを構築してください。
テストするパーツがArduino関数を呼び出す場合は、テストプログラムでモックアップの代替品を提供する必要があります。これは見かけよりはるかに少ない作業です。モックアップは実際に何もする必要はありませんが、予測可能な入力と出力をテストに提供します。
テストする独自のコードは、.pdeスケッチ以外のソースファイルに存在する必要があります。心配する必要はありません。スケッチの外部にあるソースコードを使用しても、スケッチはコンパイルされます。あなたが本当にそれに取り掛かったとき、あなたのプログラムの通常のエントリーポイントより少しだけがスケッチファイルで定義されるべきです。
あとは、実際のテストを記述し、お気に入りのC ++コンパイラを使用してコンパイルするだけです。これはおそらく実際の例で最もよく説明されています。
ここで見つかった私のペットプロジェクトの1つには、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にある私のプロジェクトを参照して、実際のテストケースをいくつか確認してください。進行中の作業をmaster以外のブランチに置いているので、それらのブランチで追加のテストを確認します。
独自の軽量テストルーチンを作成することを選択しましたが、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 ++ファイルにユニットコードを#includeして、ターゲットコードにはCファイルを使用しています。
例として、4つの7セグメントディスプレイを多重化し、1つのポートがセグメントを駆動し、もう1つのポートがディスプレイを選択するようにします。ディスプレイコードは、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 hexイメージを動的に読み込むPythonユーティリティを作成しました。以下のリンクにあるコードは、構成ファイルを介してWindowsおよびMac OS Xをサポートしています。Arduino IDEによって16進数イメージが配置されている場所を確認するには、ビルド(再生)ボタンを押す前に、Shiftキーを押します。アップロードキーを押しながらシフトキーを押して、システム/ Arduinoのバージョンでavrdude(コマンドラインアップロードユーティリティ)がどこにあるかを確認します。または、含まれている構成ファイルを確認して、インストール場所(現在はArduino 0020)を使用できます。
このプログラムは、いくつかのArduinoユニットテストの自動実行を可能にします。テストプロセスはPCで開始されますが、テストは実際のArduinoハードウェアで実行されます。通常、1つのユニットテストセットを使用して、1つの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
単体テストの各セットに対して、次の手順が実行されます。
ハードウェア固有のコードを他のコードから分離または抽象化しておくと、優れたツールがあり、最も使い慣れている任意のプラットフォームで、そのより大きな「残り」をテストおよびデバッグできます。
基本的に、できるだけ多くの既知の作業ブロックから最終コードのできるだけ多くをビルドするようにしてください。残りのハードウェア固有の作業は、はるかに簡単で高速になります。自分で既存のエミュレーターやデバイスをエミュレートすることによって、それを完了することができます。そしてもちろん、実物をどうにかしてテストする必要があります。状況に応じて、自動化がうまくいく場合とそうでない場合があります(つまり、ボタンを押して他の入力を提供するのは誰または何ですか?さまざまなインジケーターと出力を誰が何を観察して解釈するのか)。
ジェームスW. Grenningは素晴らしい本を書き、これはユニットテストの組み込みCコードについてです組込みCのために開発テスト駆動します。
私はArduinoコードを書くときにSearduinoを使用しています。Searduinoは、Arduinoシミュレーターであり、お気に入りのエディターを使用してC / C ++を簡単にハッキングできる開発環境(Makefiles、Cコード...)です。Arduinoスケッチをインポートして、シミュレータで実行できます。
Searduino 0.8のスクリーンショット:http ://searduino.files.wordpress.com/2014/01/jearduino-0-8.png
Searduino 0.9がリリースされ、最後のテストが完了するとすぐにビデオが録画されます... 1〜2日で。
シミュレーターでのテストは、実際のテストとは見なされませんが、それは確かに、愚かな/論理的な間違い(やるのを忘れる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;
};
あなたはそれを次のように単体テストします(呼ばれるテストファイル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
、構文やテスト構造は見慣れ私はいくつかの採択ので、それはだマシュー・マードックのArduinoUnitライブラリを
、彼はに言及することを彼の答え。
I / Oピン、クロック、シリアルポートなどのユニットテストの詳細については、Reference.mdを参照してください。
これらの単体テストは、ruby gemに含まれているスクリプトを使用してコンパイルおよび実行されます。設定方法の例については、README.mdを参照するか、次の例のいずれかからコピーしてください。
Arduinoのネイティブコアを提供するncoreというプロジェクトがあります。また、Arduinoコードのテストを記述できます。
プロジェクトの説明から
ネイティブコアを使用すると、PCでArduinoのスケッチをコンパイルして実行できます。通常は変更する必要はありません。標準のArduino関数のネイティブバージョンと、通常ハードウェア自体から取得されるスケッチに入力を与えるコマンドラインインタープリターを提供します。
テストを作成する場合は、http: //cxxtest.tigris.orgのcxxtestが必要です 。NCOREはcxxtest 3.10.1でテストされています。
基本的にArduinoはCとC ++で書かれていますが、ArduinoのライブラリでさえCとC ++で書かれています。したがって、簡単に言えば、コードをCおよびC ++として扱い、単体テストを実行してみてください。ここで、「ハンドル」という言葉は、serial.printlnをsysoutに、pinmodeを変数に、voidループをwhile()ループに、キーストック内で、またはいくつかの反復後に中断するなど、すべての基本構文を変更することを意味します。
私はこれが少し長いプロセスであり、それほど単純ではないことを知っています。私の個人的な経験では、一度それをやれば、これはより信頼できるようになります。
-Nandha_Frost
INOスケッチを実行してシリアル出力をチェックアウトすることに関心がある場合は、Arduino NMEAチェックサムプロジェクトにその実装を機能させています。
次のスクリプトはファイルを受け取り、Arduino CLIを使用してHEXファイルにコンパイルします。HEXファイルはSimAVRにロードされ、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
次に、HEX(またはELF)を実行するSimAVR-最新のリリースが機能しなかったため、ソースからビルドします。
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で終了します。それ以外の場合、スクリプトは失敗します。