Hallo zusammen, auf meiner Liste von Dingen, die ich unbedingt ausprobieren will, stehen Unit-Tests an recht prominenter Stelle. Bisher teste ich Funktionen eher rudimentär mit vielen Asserts und ein paar Ein-Ausgabetests, die aber nur bei der Entwicklung der Funktion genutzt werden. Allerdings interessiert mich der Ansatz mit xUnit - Tests dauerhaft neben dem eigentlichen Programm zu halten - sehr. In der Artikelsammlung findet sich ein Artikel zu uCUnit (https://www.mikrocontroller.net/articles/Unittests_mit_uCUnit), welches aber anscheinend seit 2012 verwaist ist. Ich entwickle meine Progrämmchen für ARM Cortex M3/M4 (STM32F103/STM32F446) und einen auf dem PC lauffähigen Mock-Up (mit Emulation der Benutzerschnittstelle Display, Knöpfe etc. über ein Fenster und Tastaturkommandos) in C mit dem ARM-GCC/MinGW. Unit-Tests sollten natürlich auch auf beiden Plattformen laufen. Als Build-Prozeß findet momentan der von EmBitz Verwendung. Welches Test-Framework eignet sich hierfür? Und wie habt ihr angefangen? Viele Grüße W.T.
:
Bearbeitet durch User
Walter T. schrieb: > > Welches Test-Framework eignet sich hierfür? Und wie habt ihr angefangen? > > Viele Grüße > W.T. testIdea. Allerdings sind Unit tests für typische Embedded Anwendungen nur beschränkt hilfreich, da sie nebenläufige Probleme (die speziell mit der Aussenwelt kommunizierenden uCs - alle praktisch Alle) nicht abdecken. Die Kosten-Nutzen Analyse für das doch sehr invasive Unit testen sollte also sehr sorgfältig vorgenommen werden, bevor man den Weg einschlägt. Was nicht heissen soll, dass man nicht testen sollte (je mehr Fehler man umso früher finden kann, desto besser), aber nicht Alles um jeden Preis. 5% des Fehlerpontials zu eliminieren sollte keine 95% der Entwicklungskapazität binden. Diese Zahlen sind nicht auf Unit Testing bezogen, sondern nur ein überzogenes mögliches Szenario.
Adapter schrieb: > Die Kosten-Nutzen Analyse für das doch sehr invasive Unit testen sollte > also sehr sorgfältig vorgenommen werden, bevor man den Weg einschlägt. Das ist genau mein Ziel: Feststellen, ob die Nutzung eines Test-Frameworks den Aufwand wert ist. Das Schöne am Hobby ist: Ich kann das völlig ergebnisoffen angehen.
testIdea wurde schon in den Raum geworfen. QTools von Quantum Leap scheint direkt für den in "Test Driven Development for Embedded C" von James W. Grenning beschriebenen zu orientierten Workflow gebaut zu sein. Hat irgendjemand schon irgendeinen Test-Harness wirklich benutzt/getestet und kann dazu etwas sagen?
Adapter schrieb: > Walter T. schrieb: >> >> Welches Test-Framework eignet sich hierfür? Und wie habt ihr angefangen? >> >> Viele Grüße >> W.T. Ich fahre den Ansatz, dass man für normalen hardwareunabhängigen Code seine Tests nicht auf der Zielhardware ausführen muss. Der Code wird schon modular aufgebaut und jedes Modul, sofern nötig, wird dann für sich losgelöst getestet. Dazu werden die Linkzeitabhängigkeiten durch Stubs ersetzt. Laufen lassen kann man das mit jeden beliebigen UnitTestFramework - für C/C++. Konkret schaut das bei mir so aus: Als Testfamework wird gtest/gmock genutzt. Die Sachen, die ich durch Stubs ersetzen will, werden durch freie Funktionen ersetzt, die Aufrufe in ein Mock-Singleton machen. Auf das Mocksingleton kann man dann wie üblich seine Aufrufe Testen, Actions festlegen und und und ...... Gebaut wird das ganze für die Entwicklungsmaschine und wird dann auch auf der ausgeführt. Das Ganze wirkt erst einmal sehr oversized, aber die Möglichkeiten von Gmock sind den Aufwand meiner Meinung nach wert. Grüße Oliver
Interessanter Thread. Ist es auch möglich bzw. hat es schon mal jemand versucht gtest gmock auf der Zielhardware laufen zu lassen?
Fragenüberfragen schrieb: > Ist es auch möglich bzw. hat es schon mal jemand > versucht gtest gmock auf der Zielhardware laufen zu lassen? Aufgrund der wenigen Ressourcen in µC (also in dem Fall meine ich in etwa den Footprint der Cortex M) halte ich das für unmöglich, da der Speicher-Bedarf des gtest-/gmock-Frameworks erfahrungsgemäß zu hoch ist. Achtung hier folgt mal wieder eine gepflegte Nähkästchenplauerei :D Hatte bis jetzt aber nur einen einzigen Fall, bei dem durch Unittests, die nicht auf der Zielhardware liefen, ein Fehler nicht gefunden wurde, der dann nur auf dem Zielsystem zu Problemen geführt hat (von den anderen unentdeckten Fehlen mal abgesehen :D). Wir hatten in einem Framework einen statischen Memorypool, den wir beim Initialisieren mit einer eigenen Malloc-Routine angezapft haben. Leider hat niemand daran gedacht, dass es bei ARM Probleme gibt, wenn man Blöcke heraus gibt, die byte-wise-aligned sind - die alte Unaligned-Access-Geschichte bei ARM halt. ;) Wir haben auf X86 (dort ist das egal) entwickelt und die Test natürlich nur dort laufen lassen. Aber man kann nicht alles erwischen. ;) Grüße Oliver
:
Bearbeitet durch User
Oliver J. schrieb: > die alte Unaligned-Access-Geschichte bei ARM > halt. ;) Sowas ist auch laut C bzw. C++ -Standard verboten, nur leider merkts man's auf x86 halt nicht, wenn mans falsch macht (wird nur langsamer). ARM ist hier also nicht schuld... Man kann sowas auf x86 mittels Instrumentierung finden, z.B. beim GCC & Clang indem man "-fsanitize=undefined" übergibt. Dieser fehlerhafte Code:
1 | int main () { |
2 | char array [100]; |
3 | *((int*) (array+1)) = 42; |
4 | }
|
Wird dann mit dieser Meldung quittiert:
1 | test.c:3:22: runtime error: store to misaligned address 0x7fff5d6d00d1 for type 'int', which requires 4 byte alignment |
Die Option findet auch eine Menge weiterer Fehler und ist zum Testen/Debuggen sehr hilfreich (verlangsamt aber das Programm).
Dr. Sommer schrieb: > ARM ist hier also nicht schuld Hat auch keiner behauptet. Wir sind natürlich daran Schuld gewesen. Der sanatizer war zu der Zeit leider keine wirkliche Option, da wir unter Windows mit mingw entwickelt haben. Da war keine Unterstützung geboten. Geht das mittlerweile mit mingw? Grüße Oliver
Oliver J. schrieb: > Der sanatizer war zu der Zeit leider keine wirkliche Option, da wir > unter Windows mit mingw entwickelt haben. Achso. Oliver J. schrieb: > Geht das mittlerweile mit mingw? Sieht leider nicht so aus :-/
Oliver J. schrieb: > Geht das mittlerweile mit mingw? Mit gcc-5.1.0 geht es nicht. Beim Build fehlen ihm quasi sämtliche Library-Funktionen (`__ubsan_handle_sub_overflow', `__ubsan_handle_add_overflow', `__ubsan_handle_type_mismatch' ...)
Walter T. schrieb: > testIdea wurde schon in den Raum geworfen. Sehe ich das richtig, dass ich für die Benutzung von testIdea auch zwingend deren Hardware (diese Blue Boxes) verwenden muss? Genauer: neben den Kosten für die Software fallen hier auch kosten für die Hardware an?
Wie es weiter oben schon bemerkt wurde, kann es zu unentdeckten Fehlern führen, wenn man nicht auf der Zielhardware testet. Daher wird das bei uns immer so gehandhabt. Folge ist dann natürlich das man ein ressourcesparendes Unit Testframework braucht. Ich komme im embedded Bereich mit dem folgenden gut zurecht. http://ucunit.org/ @Adapter >"Allerdings sind Unit tests für typische Embedded Anwendungen nur >beschränkt hilfreich, da sie nebenläufige Probleme (die speziell mit der >Aussenwelt kommunizierenden uCs - alle praktisch Alle) nicht abdecken." Modultest sind lediglich dafür gedacht statisch Algorithmen zu testen. Das Testen auf nebenläufige Probleme würde ich eher im Bereich des Integrationstests sehen.
Hallo zusammen, ich war zunächst sehr mißtrauisch gegenüber ucunit, insbesondere wegen des nur einzigen Commits, der zudem lange zurückliegt und der kurzen Doku. Aber: Das Ganze besteht ja ohnehin nur aus ein paar C-Dateien, Makefiles und .bat-Dateien. Die eigentliche "Test-Suite" ist eine Sammlung von Makros in uCUnit-v.h. Die einzige Beispieldatei scheint Doku genug zu sein. Also habe ich einfach mal die ersten Versuche gemacht. Einfache Funktion und Tests entwickelt (Dezibel in Leistungsfaktor umrechnen). Das ging relativ schnell. Die Makefiles sind sehr einfach gestrickt, daß sich ucunit auch problemlos in der gewohnten IDE bauen läßt. Was mir noch nicht klar ist, wie ich das auf ein ganzes Projekt beziehe, ohne daß entweder die Tests-Datei ein riesiger Spaghettiteller und Header-Grab wird oder ich die Übersicht über die Tests verliere. Außerdem ist mir noch nicht klar, ob ich irgendwelche wirkungslosen Stubs implementieren muß, um beispielsweise "UCUNIT_Tracepoint()" in meinem "Produktivcode" behalten zu können. Zuletzt ist mir noch unklar, was mit den Checklists gemeint ist und wie sie sich vom Testcase unterscheiden. Naja, wie immer, wenn man ein neues System lernt: Möglichkeiten und Probleme müssen sich erst zeigen.
:
Bearbeitet durch User
ucUnit habe ich gestern abend dann doch erst einmal beiseitegelegt. Was daran super ist: Es läßt sich kurz und schmerzlos in den Build-Prozeß der IDE einbauen. Der Fußabdruck ist winzig, und es ist für die Target-Plattform vorgesehen. Was mir auf den Geist geht: In den "FAILED"-Meldungen fehlt die Information, wie der Test fehlgeschlagen ist. Ich hätte etwas wie "expected X, got Y" erwartet. So mußte ich bei jedem Test auch erst einmal jede Menge printf-Ausgaben verteilen, um die Fehlerursache einzugrenzen. Mein nächster Testkandidat ist "unity". Es sind auch reine C-Source-Dateien, sie lassen sich auch problemlos in den bisherigen Workflow einbinden, und die "Failed"-Meldungen sind deutlich lesbarer. Zumindest die Beispieldateien scheinen auch mehr darauf gestrickt zu sein, viele kleine Tests auf viele kleine Quelltextdateien verteilen zu können. Ich bleibe dran.
Beitrag #5301469 wurde von einem Moderator gelöscht.
J. F. schrieb: > Sehe ich das richtig, dass ich für die Benutzung von testIdea auch > zwingend deren Hardware (diese Blue Boxes) verwenden muss? würde mich auch interessieren
Nach knapp zwei Wochen mit "unity" würde ich behaupten: Das ist mein Ding. Ich sehe nur einen großen und einen für mich momentan noch weniger relavanten Nachteil. Dazu später. Ich denke allerdings, das mittelfristig die Vorteile überwiegen. Wichtig war mir die Integration in mein bestehendes Projekt. Schließlich will ich durch die Geschichte unterm Strich mehr Zeit einsparen als aufwenden. Der Workflow sieht jetzt so aus: Modultests werden (momentan) nur für Funktionen geschrieben, an denen aus irgend einem Grund etwas geändert wird. Sei es, daß sie erweitert werden, ein Fehler gesucht oder sie neu angelegt werden. Und momentan noch ausschließlich auf dem PC. Bevor die Funktion angefaßt wird, wird anstelle der normalen printf-Ausgabe für die Untersuchung einfach direkt der passende Modultest angelegt und ausgeführt. Der Unterschied besteht momentan also erst einmal darin, daß ich nach getaner Arbeit die Debug-Ausgaben nicht mehr lösche, weil sie ohnehin nicht mehr an Stellen stehen, wo sie stören. Viel Zeit habe ich dadurch noch nicht gespart, aber meine Skrupel, alten Quelltext zu löschen oder anzufassen, haben massiv abgenommen. Und jetzt die beiden Nachteile: Wenn ich das richtig sehe, gibt es keine Möglichkeit, eine "code test coverage" zu instrumentieren oder zu ermitteln. Na gut. Damit kann ich (momentan) leben. Die Testabdeckung ist ohnehin noch sehr gering. Der relevantere Nachteil: Was für ein bekloppter Name! Wie soll ich bei so einem Allerweltsnamen überhaupt Informationen in einer Suchmaschine finden? Und deshalb die Frage an die Nutzer: Sehe ich das richtig, daß es keine vorgesehene Möglichkeit gibt, die TEST-Makros auszuschalten? Irgendwie nervt es mich gerade, daß ich die Test-C-Dateien alle einzeln aus dem Build-Prozeß für den Release-Code herausnehmen muß. Hier wäre es praktisch, wenn ich die TEST-Makros einfach zu einer wirkungslosen Codezeile machen könnte. Oder wie sorgt ihr dafür, daß die Tests nicht im Release-Build landen?
:
Bearbeitet durch User
Walter T. schrieb: > Irgendwie nervt es mich gerade, daß ich die Test-C-Dateien alle einzeln > aus dem Build-Prozeß für den Release-Code herausnehmen muß. Hier wäre es > praktisch, wenn ich die TEST-Makros einfach zu einer wirkungslosen > Codezeile machen könnte. Normalerweise würde man das wohl einfach durch separate Targets lösen. Ein Test-Target, ein Debug-Target und ein Release-Target. Jedenfalls empfinde ich persönlich das umbauen des CPP zu einer Art Build-System ein bisschen "von hinten durch die Brust ins Auge". Die Macher von Unity haben dafür ja eigens das Test-Build-System "ceedling" geschaffen. Ich denke aber man kann das auch mit normalem Make bzw. CMake hinwurschteln.
Christopher J. schrieb: > Normalerweise würde man das wohl einfach durch separate Targets lösen. Ja. Jein. Separate Targets benötige ich ja sowieso - allein, weil ich für zwei (vier) unterschiedliche Plattformen bauen will. Aber: Ich finde es immer angenehm, wenn ich der Build-Umgebung (egal ob make oder IDE) einfach eine komplette Verzeichnisstruktur zum Fraß vorwerfen kann, ohne jede Datei einzeln zu jedem Target an- oder abwählen zu müssen.
Joe J. schrieb: > abo! Nicht nötig. Der Drops ist gelutscht. Die automatisierten Modultests mit Unity auf dem PC und auf dem Target laufen seit Februar völlig zuverlässig und haben mir schon viele Fehler erspart oder anderweitig die Fehlersuche erleichtert, insbesondere dann, wenn man doch mal den Debugger zur Hilfe nehmen muß. Bei Funktionen, die dazu geeignet sind (also alles, was nichts mit I/O, Benutzerinteraktion oder grafischer Darstellung zu tun hat), nutze ich regelmäßig sogar test-driven-design. Bei den Funktionen, bei denen das gut geht, geht es sogar sehr gut. Alles in allem denke ich, ist der break-even-point, bei dem mehr Zeit eingespart als hineingesteckt wird, seit Monaten weit überschritten. Ich hatte zwar mal damit angefangen, meine Erfahrungen als kurzen Artikel zusammenzufassen, aber die finale Überarbeitung steht immer noch aus. Egal. Einfach selbst ausprobieren. Ist kein Hexenwerk.
Wie hast denn das mit der Instrumentierung gelöst?Über Makros dann? Der Code wird dann super unübersichtlich. Cantata hat da einen ganz netten Ansatz kostet aber richtig asche. Das Framework kann aber dafür schon einiges. Ich hasse ebenfalls die Code-Instrumentierung, da dies den Code deutlcih aufbläst.
Wie lang bist denn dran gesessen, bis du ein Konzept hattest für Deine Projekte, bist einfach nach dem Schema vorgegangen wie auf UNITY HP dargestellt? BTW: Unittests auf dem Target sind unerlässlich imho um zu sehen ob der Compiler und die CPU das machen was sie sollen, nicht wahr?
soso schrieb: > Wie hast denn das mit der Instrumentierung gelöst? Was meinst Du mit "Instrumentierung"? Wenn Du darunter das Gleiche verstehst wie ich (Extra-Code zur Bestimmung der Testabdeckung und fürs Profiling): Gar nicht. Wenn Du darunter die Möglichkeit verstehst, im Test Fehler zu triggern: Die Funktionen error() und assert() wurden durch eigene Varianten ersetzt, bei denen zur Laufzeit die Möglichkeit besteht, für einen einzigen Fehler lediglich einen Rücksprung zu erzeugen und nicht den Fault-Handler aufzurufen. Ein Test, der eine Fehlerbedingung testet, sieht dann eben so aus:
1 | TEST(bitmath, nullArgument) |
2 | {
|
3 | prepareErrorCondition(); |
4 | if( TEST_PROTECT() ) |
5 | {
|
6 | // Funktionen sind fuer zweiter Parameter 0 komplett sinnlos
|
7 | areAllBitsSet(0x0, 0x0); |
8 | }
|
9 | TEST_ASSERT_TRUE( isErrorConditionTriggered() ); |
10 | clearErrorCondition(); |
11 | }
|
soso schrieb: > Wie lang bist denn dran gesessen, bis du ein Konzept hattest für Deine > Projekte, Wenn ich den Thread überfliege, würde ich sagen: 10 Tage. Also maximal 20...30 Stunden. soso schrieb: > bist einfach nach dem Schema vorgegangen wie auf UNITY HP > dargestellt? Umgekehrt: Ich habe erst ein paar Beispiele aus dem Unity-Packages ans Laufen bekommen, abgewandelt, und dann erst verstanden, was die mir mit den Infos auf der Website sagen wollen. Hatte noch nie zuvor, auf keine Plattform, mit soetwas gearbeitet. soso schrieb: > Unittests auf dem Target sind unerlässlich imho um zu sehen ob der > Compiler und die CPU das machen was sie sollen, nicht wahr? Wahrscheinlich ist das die offizielle Begründung. Ehrlich gesagt nutze ich die Modultests auf dem Target hauptsächlich dazu, Fehler schnell und reproduzierbar zu erzeugen, um sie dann mit dem Debugger zu jagen. Unterm Strich geht das immer schneller, als mehrmals hintereinander den gleichen Programmablauf durch Benutzerinteraktion zu erzeugen. Plus ich habe die Sicherheit, daß ein einmal entfernter Fehler auch entfernt bleibt.
Walter Tarpan (Gast) schrieb: > nutze > ich die Modultests auf dem Target hauptsächlich dazu, Fehler schnell und > reproduzierbar zu erzeugen, um sie dann mit dem Debugger zu jagen. *) Nachtrag: Wenn sie durch die Modultest-Assertions nicht schon von selbst sichtbar werden.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.