Best Practice beim Unit-Test für Embedded-Entwicklung


45

Ich suche nach Best-Practice-Strategien für Unit-Testing-Code, der für eingebettete Systeme geschrieben wurde. Mit eingebettetem System meine ich Code wie Gerätetreiber, ISR-Handler usw., Dinge, die dem Metall ziemlich nahe kommen.

Die meisten Unit-Tests sind nicht möglich, ohne sie mit Hilfe eines ICE auf der Hardware zu testen. Manchmal muss die eingebettete Einheit auch an andere Reize wie mechanische Schalter, Schrittmotoren und Glühbirnen angeschlossen werden. Dies geschieht normalerweise manuell, eine Automatisierung wäre zwar großartig, aber schwer und teuer zu erreichen.

Aktualisieren

Ich bin auf ein C-Testframework gestoßen, das beim Testen von eingebetteten Projekten ziemlich erfolgreich zu sein scheint. Es nutzt die Idee, Hardware zu verspotten. Schauen Sie sich Unity , CMock und möglicherweise Ceedling an .

Update 06Jul2016

Kam über cmocka - scheint aktiver bearbeitet zu werden.


1
Unter ähnlichen Umständen haben wir uns für Cmocka
Mawg

Ich habe ein sehr ausführliches Tutorial zum Thema geschrieben: Unit-Testing von Embedded C-Anwendungen mit Ceedling
Dmitry Frank

Antworten:


28

Ich würde mich zum frühestmöglichen Zeitpunkt von den Hardware-Abhängigkeiten lösen und das System auf Software-Emulation / Test-Harness aufbauen, um alle Arten von Test-Frameworks zu ermöglichen. Oft wurde mein Entwicklungs-PC verwendet, um 95% oder mehr des gesamten Systems zu testen. Die Kosten für den zusätzlichen Aufwand (eine weitere Abstraktionsebene) wurden durch den saubereren Code, der als Ergebnis dieser Abstraktion generiert wurde, leicht zurückgewonnen.

Das Testen der wirklich baremetallischen Teile eines eingebetteten Systems ist normalerweise eine separate Anwendung (Unit Test?), Die die Firmware weit über das hinaus bringt, was die Anwendungen sich erhoffen können. Automatisierung ist möglich - kostenpflichtig, aber nicht typisch.

Es sei denn, Sie haben das Budget, einen Hardware-Kabelbaum für den Komponententest einschließlich ICE zu bauen. Dies ist absolut in Ordnung, da die Funktionstests im Allgemeinen klein sind.


Dies in Kombination mit der Antwort von Jonathan Cline Iee ist eine erfolgreiche Strategie. Verwenden Sie die Abstraktion, um den größten Teil des Codes testbar zu machen, und testen Sie mit einem einfachen Testframework die nicht abstrahierbaren Bits auf der realen Hardware. Ich persönlich habe diese Arbeit mit mehreren Plattformen gesehen.
Tim Williscroft

3
Jedes einzelne Produkt, das wir herstellen, verfügt über Hardware Abstraction Layer mit der Implementierung von Treibern für die Zielplattform und den PC. Auf diese Weise können wir problemlos Komponententests durchführen. Weitere Vorteile: Wir können auch schnelle Systemtests durchführen und den größten Teil der Software ohne Hardware entwickeln (wie es später verfügbar ist).
15.

15

Ein notwendiges Werkzeug zur Entwicklung ist ein Signalinjektor. Das eingebettete System kann auf irgendeine Weise mit einem Host-System verbunden werden (normalerweise über einen seriellen Port, der für das Debuggen reserviert ist). Verwenden Sie diese Option, um Testdaten zu senden (die beste Option ist im knappen ASCII-Format, damit sie auch von Menschen problemlos simuliert werden können).

Ich stimme diesem Teil Ihrer Frage überhaupt nicht zu: "Automatisierung wäre großartig, aber schwer und teuer zu erreichen."

Wenn TeraTerm als serieller Port-Signalinjektor verwendet wird und einige TeraTerm-Makros geschrieben werden (dauert ca. 20 Minuten), gibt es eine riesige Reihe automatisierter Tests, die für alle Teile eines eingebetteten Systems ausgeführt werden können - ob Treiberschicht, Betriebssystem, Schicht 4-5 usw. TeraTerm: http://en.sourceforge.jp/projects/ttssh2/

Wenn auf dem eingebetteten System keine serielle Schnittstelle verfügbar ist, konvertieren Sie die Daten der USB- / seriellen Schnittstelle mit einem Hardware-Tool in digitale Signale (auch kostengünstig und einfach zu erreichen). Während Sie dies lesen, benutze ich eine 30-Dollar-Mikrocontroller-Platine (UBW: http://www.schmalzhaus.com/UBW32/ ), um ein eingebettetes System für die Produktion zu testen, indem über TeraTerm-Makros, die über USB / seriell an gesendet werden, Stimulus injiziert wird der Mikrocontroller, auf dem eine modifizierte Firmware ausgeführt wird, die die digitalen Eingänge ausübt und die digitalen Ausgänge des eingebetteten Zielsystems überwacht. In Verbindung damit haben wir ein Python-Skript entwickelt (verwendet pyserial und pexpect), um die Dateninjektion und Datenvalidierung zu automatisieren. Nichts davon ist schwer und nichts davon ist teuer. Nach meiner Erfahrung geben die Manager viel Geld aus (z. B. 30.000 US-Dollar für Testgeräte), wenn das Testteam unerfahren ist und sich diese einfachen Lösungen nicht vorstellen kann - leider enthalten die allgemeinen Big-Iron-Geräte häufig keine Testfälle die den Worst-Case-Timing / etc des Zielsystems abfangen. Daher ist die kostengünstige Methode für die Testabdeckung vorzuziehen. Glaub es oder nicht.


1
Nun, ich habe die teure Erklärung abgegeben, als ich in der Automobilindustrie arbeite und alles muss deterministisch und wiederholbar sein und normalerweise braucht es ein paar Ingenieure, um sich zu entwickeln. Auch wenn mehr Elemente in der Testkette verwendet werden, wird die Wartung zu einem Problem. Vielen Dank, dass Sie uns über die UBW informiert haben, sie scheint eine gute Option zu sein.
tehnyit

2
Lass mich einfach nicht mit LabView anfangen ... das ist normalerweise schreckliches Zeug.
Jonathan Cline IEEE

1
Unsere Testingenieure lieben LabView, ich verstehe es selbst nicht ganz.
tehnyit

Dies ist ziemlich ähnlich zu dem, was ich für verschiedene Tests mache, nur ich benutze Python und deren serielle Bibliotheken. Ich könnte dann meine Low-Level-Tests zusammen mit etwas wie Flask / Qt in Pythons Unit-Tester einbinden, um ein einfach zu bedienendes Frontend zu erstellen.
radix07

5

Das ist ein sehr schwieriges Problem.

Ich habe ein Unit-Testing-Kabel für ein Embedded-System entwickelt, mit dem Hardware-Ereignisse / Interrupts simuliert und der Zeitpunkt der Ausführung gesteuert werden kann (um sicherzustellen, dass alle möglichen Interleavings aufgrund der Parallelität abgedeckt sind) Programmierer mehr als 2 Jahre, um es tatsächlich zu implementieren und zum Laufen zu bringen. Dieses Projekt ist eine Eigenentwicklung, aber ein ähnliches Projekt (einfacher im Design) ist hier verfügbar .

Also ja, Automatisierung wäre toll. Ja, es ist sehr schwer und teuer zu erreichen. Ja, manchmal muss man das machen. Selten jedoch ist es meiner Erfahrung nach in den meisten Fällen schneller und billiger, die Schrittmotoren und Glühbirnen zu verwenden und alles manuell arbeiten zu lassen.


Ich habe festgestellt, dass das Gerät manuell fehleranfällig ist, normalerweise entweder beim Erzeugen des Stimulus oder beim Messen der Ergebnisse. Besonders dann, wenn der Komponententest kompliziert ist. Wenn Sie den Unit-Test erneut durchführen müssen, ist er noch fehleranfälliger.
Tehnyit

@tehnyit - ja, das war der Grund, warum wir uns entschieden haben, das Automatisierungssystem zu entwickeln. Manchmal können Dinge überhaupt nicht manuell erledigt werden, aber der Komponententest muss umfassend sein und Timing-Probleme abdecken. Dann haben Sie nicht viel Auswahl, aber Automatisierung auf dieser Ebene ist eine sehr teure Sache.
Littleadv

4

Edit: Meine Antwort ist in der Nähe von Mattnz, denke ich ...


Ich möchte dieses Problem mit anderen in Verbindung bringen, mit allen Tests, die von etwas außerhalb Ihres Codes abhängen (wie der Systemuhr, einem dauerhaften Dateisystem oder einer Datenbank, die Kontaktaufnahme mit einem externen Webdienst ...). Ich schlage die gleiche Richtlinie für alle vor, isoliere die beiden Ebenen in zwei Codeebenen.

Testen einer einzelnen externen Operation

Möglicherweise möchten Sie jede Operation physisch testen. Überprüfen Sie, ob die Systemuhr die richtige Zeit angibt, ob sich eine Datei tatsächlich an das erinnert, was geschrieben wurde, ob ein Gerät eine einzelne Operation empfängt ...

Diese Tests:

  • sollte so einfach wie möglich sein: kein Algorithmus, keine Bedingung oder Schleife
  • Dies kann auftragsabhängig und maschinenabhängig sein. Befolgen Sie daher eine strikte Reihenfolge und wiederholen Sie dies auf jeder Hardware
  • sind im Verlauf Ihres Projekts meist stabil, sodass Sie sie nicht so oft ausführen müssen
  • Daher ist es eine Option, sie manuell auszuführen. Automatisierung ist noch besser, wenn nicht zu komplex
  • Beachten Sie, dass das , was getestet wird, nicht Ihr Code ist. Es ist ein Tool, das Ihr Code benötigt. Das Testen kann also für Sie optional sein. Möglicherweise wurde es von einem anderen Team durchgeführt.

Testen der Logik (Code, Algorithmus), die die externen Operationen verbindet

Indem Sie eine Codeschicht für die eigentlichen externen Vorgänge haben und diese hinter einer Schnittstelle verbergen, die Sie leicht verspotten können, ist Ihre Logik nicht mehr von den tatsächlichen physischen Geräten abhängig ...

Sie können einfach testen, wie bei jedem normalen Projekt, Sie sind nicht mehr in einem schwer zu testenden Code eingebettet .


3

Embedded-CPU-Simulatoren können im Allgemeinen so programmiert werden, dass sie auch Hardware simulieren. Alle anderen Virtualisierungstechnologien als Xen tun dies. Sie müssen jedoch Code schreiben, der vorgibt, einige Register an einer physischen Adresse oder auf x86 eine Adresse am E / A-Bus zu haben, und dann auf Lese- und Schreibvorgänge an diesen Adressen antworten, als wäre Ihre Software eine physische Chip, auf dessen Steuer- und Statusregister zugegriffen wurde.

Wenn Sie dies tun möchten, würde ich vorschlagen, QEMU zu ändern. Aber es wäre nicht einfach. Dies geschieht in der Regel nur, wenn Sie einen benutzerdefinierten Chip mit einem Mikrocontroller und einigen anderen Kernen für Ihre E / A entwerfen.

Das von ARM Holdings vertriebene Entwicklungssystem sieht dies vor und ist wahrscheinlich einfacher zu handhaben als das Hacken auf QEMU, aber sehr teuer.

Es gibt mehrere Open Source ARM-Emulatoren, die eine einzige Unterroutine ausführen, die selbst andere Unterroutinen aufrufen kann, die Sie zum Debuggen und Optimieren der Leistung von Unterroutinen verwenden können, die nicht vom Hardwarezugriff abhängig sind. Ich habe eine davon mit großem Erfolg genutzt, um einen AES-Verschlüsseler für ARM7TDMI zu optimieren.

Sie können ein einfaches Unit-Test-Harness in C oder C ++ schreiben, die zu testende Klasse oder Subroutine damit verknüpfen und dann im Simulator ausführen.

Ich habe jahrelang über ein ähnliches Problem nachgedacht, wie man Linux- oder Mac OS X-Kernel-Code testet. Es sollte möglich sein, aber ich habe es nie versucht. Eine Möglichkeit besteht möglicherweise darin, einen vollständigen Kernel zu erstellen, anstatt den Code isoliert zu testen, wobei das Unit-Test-Framework direkt in den Kernel eingebunden ist. Sie würden dann die Unit-Tests von einer externen Schnittstelle aus starten.

Vielleicht wäre es produktiver, ein Tool zur Codeabdeckung zu verwenden und dann Ihre Firmware als vollständiges Paket über die externe Schnittstelle zu testen. Das Coverage-Tool findet Codepfade, die noch nicht getestet wurden, sodass Sie zusätzliche externe Tests hinzufügen können, um mehr Coverage zu erhalten.


3

Wie bei nicht eingebettetem TDD sind Scheinobjekte definitiv Ihr Freund.

Halten Sie die Schnittstelle zu Ihrer zugrunde liegenden Hardware sauber und einfach, damit alles, was über der niedrigsten Ebene liegt, verfälscht wird. Wenn Sie Ihre eingebettete Anwendung mit Blick auf die Testbarkeit entwerfen, werden die Tests immer reibungsloser verlaufen .

Nur weil Sie möglicherweise erst spät im Projekt online testen können, bedeutet dies nicht, dass Sie auch keine Reihe von Online-Tests vorbereiten sollten.

Diese sollten (zunächst) nur die Bits testen müssen, die offline nicht getestet werden konnten. Klar, es ist kein TDD (da Sie die Tests im Voraus erstellen), aber Ihre Offline-TDD-Entwicklung sollte Ihnen eine gute Vorstellung davon geben, wie Ihre Hardwareschnittstelle aussehen muss und somit welche Onlinetests Sie durchführen müssen.

Wenn die Online-Entwicklung viel mehr kostet als die Offline-Entwicklung (wie bei meiner Arbeit), können Sie dadurch viel Zeit sparen, wenn Sie online über eine Reihe von Tests verfügen, die gut verstanden werden.


+1, um Scheinobjekte auf den Teller zu bringen, @mark. Das einzige Problem ist, dass die Genauigkeit der Scheinobjekte sichergestellt werden muss, was bedeutet, dass das Verständnis für das zu verspottende Objekt sehr tief sein muss. Dies ist gut, da der Entwickler gezwungen ist, das Verhalten der externen Objekte zu verstehen, mit denen er eine Verbindung herstellt.
Tehnyit

1

Bei der Embedded-Entwicklung führen Sie häufig Boundary-Scans durch , um zu überprüfen, ob die gesamte Anwendung (einschließlich der Hardware) funktioniert. Siehe auch JTAG für das System-Debugging.

Das Testen von reinen Softwareroutinen ohne Verbindung zur Hardware kann mit einem Standard-C-Unit-Test-Framework wie Check durchgeführt werden . Achten Sie jedoch auf Speicherbeschränkungen (insbesondere Stapelspeicher usw. auf kleinen Geräten). Kennen Sie Ihre Verträge! Sie können auch versuchen, die Softwareroutinen von der Hardware zu abstrahieren, um eine größere Testabdeckung zu erzielen. Dies ist jedoch in der Regel in Bezug auf die Leistung auf eingebetteten Geräten wie kleinen PICs oder AVRs kostspielig. Sie können jedoch Hardware-Ports verspotten, um eine größere Abdeckung zu erzielen (und natürlich können Sie diese Verspottung auch testen).

Sie können auch versuchen, Emulatoren für die Chip- oder Schaltkreissimulatoren zu verwenden, diese Tools sind jedoch teuer (insbesondere in Kombination) und kompliziert.


Stimmen Sie mit Boundary Scan und JTAG überein, aber aufgrund des Designs der Hardware ist dies nicht immer möglich oder verfügbar.
Tehnyit
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.