Ich habe bisher kein Embedded-Team gesehen, das Testen als unwichtig erachtet. Im Gegenteil, jeder im Entwicklungsteam weiß, wie wichtig das Testen für ein stabiles Gesamtsystem ist.
Das Testen beschränkt sich aber in manchen Fällen leider noch auf „works on my machine“. Läuft es auf dem eigenen Setup auf dem Schreibtisch, ist alles in Ordnung.
Dedizierte Testsysteme für automatisiertes Testen oder definierte manuelle Testroutinen tragen nicht nur durch reproduzierbare Testumgebungen dazu bei, Fehler zu finden, sondern stärken auch das Vertrauen der Entwickler in ihren eigenen Code.
Bauchgefühl statt echter Zuversicht – Unsicherheit in der Entwicklung
Der größte Nachteil fehlender Tests ist meiner Meinung nach die Unsicherheit.
Jeder Entwickler kennt den Moment nach einem Commit:
„Ich hoffe mein Fix, hat nicht irgendwo anders etwas kaputt gemacht“
Dieses Gefühl entsteht, wenn:
- es keine definierten Testfälle gibt
- Tests nach Gefühl durchgeführt werden
- Ergebnisse nicht reproduzierbar sind
- kein Testreport ausgegeben wird, der Fehler sichtbar macht
Um diese Unsicherheit zu eliminieren, braucht es eine klare Testroutine, bestehend aus konkreten und detaillierten Testschritten und einen sauberen Testreport.
Eine einfache Teststrategie
Bereits eine einfache Teststrategie liefert große Wirkung.
Sobald die grundlegenden Tests kontinuierlich in einer definierten Testumgebung ausgeführt werden, steigt das Vertrauen im gesamten Team.
Kontinuierliche Testausführungen sollten, sofern möglich und sinnvoll, in die CI/CD-Umgebung integriert werden.
Für Unittests ist das meist unkompliziert, diese lassen sich direkt auf dem Host ausführen, wenn die Software von der Hardware abstrahiert ist. Unittests sollten also portierbar, leicht auszuführen und am besten schnell sein, denn als Entwickler will man nicht stundenlang nach einem Commit warten, um zu sehen, ob es zu Fehlern kam. Integrationstests hingegen werden meist in einer simulierten Testumgebung (Software-in-the-Loop (SiL)) oder auf echter Hardware (Hardware-in-the-Loop (HiL)) ausgeführt. Welche Umgebung sinnvoll ist, hängt von Betriebssystem, Hersteller, Programmiersprache, Anwendungsfall und Komplexität ab.
Verschiedene Testumgebungen lassen sich kombinieren, sollten aber bewusst gewählt werden, wenn der zusätzliche Nutzen den Mehraufwand rechtfertigt.
Je mehr Umgebungen und Testfälle man parallel betreibt, desto höher wird der Wartungsaufwand im QA-Prozess.
Auf die Unterschiede zwischen Host-, SiL- und HiL-Umgebungen gehe ich in einem eigenen Artikel ein.
Von manuellen zu automatisierten Integrationstests
Bei der Definition von Integrationstests gilt als Faustregel:
Zuerst manuell testen, dann automatisieren.
Der richtige Ablauf ist:
- Dokumentieren, was man aktuell manuell testet
- Testschritte so detailliert beschreiben, dass sie auch ein Nicht-Entwickler ausführen könnte
- Diese Schritte mit verschiedenen Softwareversionen durchgehen
- Randbedingungen und typische Fehlerbilder notieren
- Testfälle identifizieren, die:
- den größten Zeitaufwand haben
- den größten Mehrwert bringen
- am leichtesten automatisierbar sind
Erst dann beginnt die eigentliche Automatisierung.
Manuelle Tests sind also Kandidaten für eine Automatisierung. Man sollte immer mit manuellen Tests starten, bevor man automatisiert, außer es sind triviale Unittests.
Diese komplexeren Tests werden meist in SiL- oder HiL-Testumgebungen ausgeführt. Je nach Anwendungsfall eignet sich auch hier wieder eine der beiden Umgebungen oder sogar ein Mix besser.
HiL-Testumgebungen
Reine Software-in-the-Loop (SiL) Tests reichen oft nicht aus, wenn ein Testen näher an der realen Hardware erforderlich ist oder die Entwicklung einer Simulationsumgebung zu aufwendig wäre.
In diesen Fällen greift man auf Hardware-in-the-Loop (HiL) Testumgebungen zurück, wobei in der Praxis sehr häufig ein Mix aus HiL- und SiL-Komponenten zur Anwendung kommt.
Vorteile des HiL/SiL-Mix
Dieser hybride Ansatz wird oft gewählt, um sowohl Sicherheit zu gewährleisten als auch die Testeffizienz zu steigern. Ein gutes Beispiel ist die Ansteuerung eines Batteriespeichers:
- Sicherheit: Die sofortige physische Einbindung einer realen Batterie in die HiL-Umgebung birgt ohne hinreichende Sicherheitsvorkehrungen erhebliche Risiken und gefährdet Menschenleben.
- Effizienz: Tests mit einem physischen Batteriespeicher sind extrem langwierig, da Zustandswechsel (z. B. der Sprung von 3% auf 95% Ladezustand) nicht künstlich herbeigeführt werden können, sondern realistische Ladezyklen abgewartet werden müssten.
Daher wird der Batteriespeicher in der Regel simuliert, während das zu testende Steuergerät physisch vorhanden ist. Für spezifische Validierungen, die eine reale Batterie erfordern, kann ein separater Teststand mit ausreichenden Sicherheitsmaßnahmen parallel aufgebaut werden.
Komponenten einer HiL-Umgebung
Für die Durchführung von HiL-Tests sind folgende Komponenten zwingend erforderlich:
- DuT (Device under Test): Die zu testende Hardware (z. B. ein Evaluation Board oder eine eigene Custom PCB).
- Flash-Mechanismus: Eine automatisierte Möglichkeit, das DuT mit der zu testenden Software zu flashen.
- Spannungsquelle: Zur Versorgung des DuTs und zum Reset.
- Testsystem-Verbindung: Eine Schnittstelle zwischen DuT und Testsystem, um Tests auszuführen und die Ergebnisse zu validieren für ein Embedded Linux eignet sich bspw. eine SSH-Verbindung.
Optionale Komponenten zur Ergänzung:
- Signalgeneratoren
- Oszilloskope
- Spectrum Analyzer
- Logic Analyzer
- Gegenschnittstellen (UART, CAN, SPI, I²C usw.)
- weitere Spannungsquellen und Lasten
- etc.
Auch diese Tests lassen sich in die CI/CD-Pipeline (Continuous Integration/Continuous Delivery) integrieren. Häufig werden diese nach jedem Commit auf spezielle Branches, über Nacht oder beim Merge Request auf „main“ ausgeführt. Dies ist abhängig vom Umfang und der Dauer des ausgeführten Tests. Bei komplexen Systemen, die aus mehreren Subsystemen bestehen, ist es oft sinnvoll, Integrationstests in mehrere Stufen zu unterteilen. Dies ermöglicht eine schrittweise und effizientere Validierung.
Es macht auch durchaus Sinn, mehrere HiL-Testumgebungen mit verschiedenem Funktionsumfang aufzubauen, um die Auslastung zu maximieren und Tests häufiger ausführen zu können. Beispielsweise sollten langwierige Tests und schnelle Integrationstests auf separaten Umgebungen oder zumindest zu verschiedenen Zeitpunkten ausgeführt werden (z. B. nach jedem Commit oder als Nightly).
Hardware-in-the-Loop-Testumgebungen sind sehr spezifisch auf das zu testende Gerät bzw. auf das zu testende System ausgerichtet. Es gibt aber auch Teile der HiL-Architektur, die gleich bleiben und wiederverwendbar sind. Darauf werde ich in einem weiteren Artikel näher eingehen.
Fazit
Unsicherheit und fehlendes Vertrauen entsteht nicht durch mangelnde Fähigkeiten der Entwickler, sondern durch fehlende Struktur im Testprozess. Mit einer klar definierten Testroutine, reproduzierbaren Testumgebungen und einfachen, kontinuierlich ausgeführten Tests lässt sich bereits ein Großteil dieser Unsicherheit beseitigen.
Unittests liefern dabei den schnellsten Einstieg. Werden sie durch manuelle Testdefinitionen, SiL-Tests und HiL-Setups ergänzt, entsteht Schritt für Schritt ein stabiler und skalierbarer QA-Prozess.
Testautomatisierung beginnt nicht mit Tools, sondern mit klaren, nachvollziehbaren manuellen Tests, darauf baut zuverlässige Automatisierung auf.

