Führen Sie keine Komponententests auf dem Arduino-Gerät oder Emulator durch
Der Fall gegen Mikrocontroller-Geräte- / Emulator- / Sim-basierte Tests
Es wird viel darüber diskutiert, was Unit-Test bedeutet, und ich versuche hier nicht wirklich, darüber zu streiten. Dieser Beitrag ist nicht zu
sagen Sie zu vermeiden , alle praktischen Tests auf Ihre ultimative Ziel - Hardware. Ich versuche, einen Punkt zur Optimierung Ihres Entwicklungs-Feedback-Zyklus zu machen, indem ich Ihre Zielhardware aus Ihren alltäglichsten und häufigsten Tests eliminiere. Es wird angenommen, dass die zu testenden Einheiten viel kleiner sind als das gesamte Projekt.
Der Zweck von Unit-Tests besteht darin, die Qualität Ihres eigenen Codes zu testen. Unit-Tests sollten im Allgemeinen niemals die Funktionalität von Faktoren testen, die außerhalb Ihrer Kontrolle liegen.
Stellen Sie sich das so vor: Selbst wenn Sie die Funktionalität der Arduino-Bibliothek, der Mikrocontroller-Hardware oder eines Emulators testen, können solche Testergebnisse absolut nichts über die Qualität Ihrer eigenen Arbeit aussagen. Daher ist es weitaus wertvoller und effizienter, Komponententests zu schreiben, die nicht auf dem Zielgerät (oder Emulator) ausgeführt werden.
Häufige Tests auf Ihrer Zielhardware haben einen schmerzhaft langsamen Zyklus:
- Passen Sie Ihren Code an
- Kompilieren und auf das Arduino-Gerät hochladen
- Beobachten Sie das Verhalten und raten Sie, ob Ihr Code das tut, was Sie erwarten
- Wiederholen
Schritt 3 ist besonders unangenehm, wenn Sie erwarten, Diagnosemeldungen über die serielle Schnittstelle zu erhalten, Ihr Projekt jedoch die einzige serielle Hardware-Schnittstelle Ihres Arduino verwenden muss. Wenn Sie der Meinung sind, dass die SoftwareSerial-Bibliothek hilfreich sein könnte, sollten Sie wissen, dass dies wahrscheinlich alle Funktionen stört, die ein genaues Timing erfordern, z. B. das gleichzeitige Generieren anderer Signale. Dieses Problem ist mir passiert.
Wenn Sie Ihre Skizze mit einem Emulator testen und Ihre zeitkritischen Routinen perfekt laufen, bis Sie sie auf das eigentliche Arduino hochladen, ist die einzige Lektion, die Sie lernen werden, dass der Emulator fehlerhaft ist - und dies immer noch zu wissen verrät nichts über die Qualität Ihrer eigenen Arbeit.
Was soll ich tun, wenn es albern ist, auf dem Gerät oder Emulator zu testen ?
Sie verwenden wahrscheinlich einen Computer, um an Ihrem Arduino-Projekt zu arbeiten. Dieser Computer ist um Größenordnungen schneller als der Mikrocontroller. Schreiben Sie die Tests, die auf Ihrem Computer erstellt und ausgeführt werden sollen .
Denken Sie daran, dass das Verhalten der Arduino-Bibliothek und des Mikrocontrollers entweder korrekt oder zumindest durchweg falsch sein sollte .
Wenn Ihre Tests eine Ausgabe liefern, die Ihren Erwartungen widerspricht, liegt wahrscheinlich ein Fehler in Ihrem getesteten Code vor. Wenn Ihre Testausgabe Ihren Erwartungen entspricht, sich das Programm jedoch beim Hochladen auf das Arduino nicht richtig verhält, wissen Sie, dass Ihre Tests auf falschen Annahmen beruhten und Sie wahrscheinlich einen fehlerhaften Test haben. In beiden Fällen haben Sie echte Einblicke in Ihre nächsten Codeänderungen erhalten. Die Qualität Ihres Feedbacks wurde von " etwas ist kaputt" auf "dieser spezifische Code ist kaputt" verbessert .
Erstellen und Ausführen von Tests auf Ihrem PC
Als erstes müssen Sie Ihre Testziele identifizieren . Überlegen Sie, welche Teile Ihres eigenen Codes Sie testen möchten, und stellen Sie dann sicher, dass Sie Ihr Programm so erstellen , dass Sie einzelne Teile zum Testen isolieren können .
Wenn die Teile, die Sie testen möchten, Arduino-Funktionen aufrufen, müssen Sie in Ihrem Testprogramm Ersatzmodelle bereitstellen. Das ist viel weniger Arbeit als es scheint. Ihre Modelle müssen eigentlich nichts anderes tun, als vorhersehbare Ein- und Ausgaben für Ihre Tests bereitzustellen.
Jeder eigene Code, den Sie testen möchten, muss in anderen Quelldateien als der .pde-Skizze vorhanden sein. Keine Sorge, Ihre Skizze wird auch mit Quellcode außerhalb der Skizze kompiliert. Wenn Sie wirklich dazu kommen, sollte in der Skizzendatei kaum mehr als der normale Einstiegspunkt Ihres Programms definiert werden.
Sie müssen nur noch die eigentlichen Tests schreiben und sie dann mit Ihrem bevorzugten C ++ - Compiler kompilieren! Dies lässt sich wahrscheinlich am besten anhand eines Beispiels aus der Praxis veranschaulichen.
Ein aktuelles Arbeitsbeispiel
Eines meiner hier gefundenen Lieblingsprojekte enthält einige einfache Tests, die auf dem PC ausgeführt werden. Für diese Antwort werde ich nur erläutern, wie ich einige Funktionen der Arduino-Bibliothek und die Tests, die ich zum Testen dieser Modelle geschrieben habe, nachgebildet habe. Dies steht nicht im Widerspruch zu dem, was ich zuvor gesagt habe, dass ich den Code anderer Leute nicht getestet habe, weil ich derjenige war, der die Modelle geschrieben hat. Ich wollte sehr sicher sein, dass meine Modelle korrekt waren.
Quelle von mock_arduino.cpp, die Code enthält, der einige von der Arduino-Bibliothek bereitgestellte Unterstützungsfunktionen dupliziert:
#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);
}
Ich verwende das folgende Modell, um eine lesbare Ausgabe zu erzeugen, wenn mein Code Binärdaten auf das serielle Hardwaregerät schreibt.
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;
und schließlich das eigentliche Testprogramm:
#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();
}
Dieser Beitrag ist lang genug. Weitere Informationen zu Testfällen finden Sie in meinem Projekt auf GitHub . Ich behalte meine laufenden Arbeiten in anderen Zweigen als dem Master vor. Überprüfen Sie diese Zweige daher auch auf zusätzliche Tests.
Ich habe mich entschieden, meine eigenen leichten Testroutinen zu schreiben, aber es sind auch robustere Unit-Test-Frameworks wie CppUnit verfügbar.