Forum: Mikrocontroller und Digitale Elektronik Framework zur Reduktion von Abhängigkeiten bei Software Tests


von Basti B. (basti195) Benutzerseite


Lesenswert?

Hallo zusammen,
im Rahmen meiner Bachelor Arbeit habe ich mich mit Teststrategien für 
Embedded Systeme auseinander gesetzt und unter anderem Analysiert wieso 
vergleichsweise wenig Software automatisiert getestet wird.
Ein Grund hierfür ist meiner Meinung nach, dass in vielen (Legacy) 
Projekten interne Software-Abhängigkeiten das erstellen von Testfällen 
erschweren.

Idealer Weise wird zur Reduktion von Abhängigkeiten die „Link-Time 
Substitution“(LTS) eingesetzt. Dies setzt aber voraus, dass bereits bei 
erstellen der Module auf Testbarkeit geachtet wird und einzelne 
Source-Files nur wenige Funktionalität realisieren.

Oftmals kommt es jedoch vor, dass einzelne Source-Files viele Funktionen 
haben, sodass hier die LTS kaum eingesetzt werden kann.
Hier setzt meine Arbeit an: ich habe ein System entworfen, welches in 
der Lage ist über den GDB File interne Abhängigkeiten aufzulösen und so 
die Testbarkeit zu vereinfachen.

Nun habe ich die Erlaubnis erhalten, dass das Projekt veröffentlicht 
wird:

https://github.com/intel/Dependency-Reduction-Unit-Framework/

Ich hoffe ich kann hiermit ein wenig zur verbesserten Testabdeckung 
beitragen.
Für Fragen stehe ich gerne zur Verfügung
Basti195

von Peter D. (peda)


Lesenswert?

Basti B. schrieb:
> Teststrategien für
> Embedded Systeme auseinander gesetzt und unter anderem Analysiert wieso
> vergleichsweise wenig Software automatisiert getestet wird.

In Embedded Systemen spielt die Zeit einen sehr große Rolle und nicht 
nur das Ergebnis. Automatisierte Tests testen immer genau in der 
gleichen verhersehbaren Reihenfolge und das macht es schwer, Deadlocks, 
Race Conditions usw. zu finden. Ein Ergebnis kann zwar richtig sein, das 
Programm reagiert aber falsch, wenn es zu spät vorliegt. Insbesondere 
äußere Ereignisse von Sensoren, gestörte Daten oder menschliche Eingaben 
lassen sich schwer automatisieren.
Dadurch werden Tests für Embedded Systeme sehr komplex.

Beitrag #6331887 wurde von einem Moderator gelöscht.
von Stefan F. (Gast)


Lesenswert?

Peter D. schrieb:
> ... viel sinnvolles Zeug
> Dadurch werden Tests für Embedded Systeme sehr komplex.

Genau das gleiche trifft auch auf Enterprise Anwendungen zu. 
Automatisiertes Testen scheitert meist an den unvorhergesehenen 
Situationen, auf die man beim manuellen testen (meist versehentlich) 
stößt. Und dann gibt es noch Stellen Wo manuelle Interaktion zwingend 
notwendig ist (in meinem Fall z.B. die Bestätigung eines Zahlvorgangs 
bei Paypal). Das soll und kann man gar nicht automatisieren.

Klar kann man Papal durch einen Mock ersetzen, aber dann merkt man auch 
nicht, wenn die Anwendung irgendwann plötzlich nicht mehr kompatibel ist 
weil Paypal etwas geändert hat.

Bei Elektronik ist es doch nicht anders. Da kann man manche Dinge auch 
nur mit der echten Hardware testen, und damit meine ich nicht nur die 
Elektronik sondern auch die Mechanik samt Umgebung.

Automatisierte Tests sind gut und schön, aber sie können manuelle Tests 
nur zum Teil ersetzen. Die Verkäufer der Automaten sehen das natürlich 
ganz anders.

von MaWin (Gast)


Lesenswert?

Basti B. schrieb:
> unter anderem Analysiert wieso vergleichsweise wenig Software
> automatisiert getestet wird.

Ähm, seit ISO9001 und Scrum müssten automatisierte Tests Standard sein, 
schliesslich kann sich keiner leisten, einen Bug, der irgendwann gefixt 
wurde, erneut als Bug auszuliefern.

Das übliche Vorgehen dürfte wohl Jenkins im nightly build sein, und bei 
jedem Programm ohne Benutzeroberfläche reicht es, Funktionen mit 
passenden Parametern aufzurufen.

Das Problem ist, dass viele Programme und Module ohne ein grosses 
drumherum nicht laufen. Dafür möchten einem Frameworks ja Testklassen 
empfehlen.

Unser Vorgehen ist es, für Libraries (DLL oder statisch gelinkt) jeweils 
ein (sonst nicht vetwendetes) Hauptprogramm zu schreiben, mit dem 
scriptgesteuert jede Funktion/Methode mit jedem Parametersatz aufrufbar 
ist, die Ergebnisse, auch Exceptions, auf Erwartungswerte geprüft 
werden. Old-School, von der Kommandozeile. Bei Embedded eben über den 
Debugger oder Simulator.


Der Programmierer schreibt schon Testfälle, in denen jede Verzweigung in 
seinem Programm zumindest ein Mal im if und ein mal im else Fall 
durchlaufen wird, jede Exception ausgelöst wird.

Genau so wird das Hauptprogramm getestet.

Damit weiss man aber immer noch nicht, ob jeder Zweig der 
Systemfunktionen, die sich auch noch bei jedem update ändern können, 
gecheckt wird.

Bugs kommen also auch von den Anwendern als Meldung, und als erstes sind 
die dann mit dem Test-Tool nachzuvollziehen. Werden sie gefixt, 
bestätigt das Test-Tool mit genau dem Testfall den fix. Und wird dauernd 
nachgecheckt.

Da ggf. Abhängigkeiten vom Betriebssystemversion und -updatestand 
bestehen, laufen einige Tests in eingefrorenen virtuellen Maschinen, das 
kann Jenkins ja.

Um Probleme mit Multithreading nachstellen haben wir Spezialfunktionen 
drin, die gezielte Prozessabläufe erlauben.

Schwieriger ist es, Programme mit Bedienoberfläche und graphischem 
Output zu kontrollieren, dafür gibt es aber Hilfsprogramme. Hiess eins 
davon WinRunner ?

Jedenfalls gut, dass du dir Gedanken machst, und was anbietest.

von Stefan F. (Gast)


Lesenswert?

MaWin schrieb:
> Der Programmierer schreibt schon Testfälle, in denen jede Verzweigung in
> seinem Programm zumindest ein Mal im if und ein mal im else Fall
> durchlaufen wird, jede Exception ausgelöst wird.

So stellt man sich das vor. Und wer bezahlt das?

von MaWin (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> So stellt man sich das vor. Und wer bezahlt das?

Dank Scrum der Auftraggeber, denn der hat den Auftrag unterschrieben, 
ohne vorher zu wissen was er zu zahlen hat, da er ja auch nicht gesagt 
hat, was er haben will. Man hat ihm nur gesagt, wie man es bauen würde.

Aber: ordentliche Tests zahlen sich langfristig aus. Man glaubt gar 
nicht, wieviele Fehler in einem selbstgeschriebenen strcmp o.ä. stecken 
können.

von Stefan F. (Gast)


Lesenswert?

MaWin schrieb:
> Dank Scrum der Auftraggeber, denn der hat den Auftrag unterschrieben,

Ich lebe leider nicht in der heilen Welt, wo man nach Lehrbuch arbeiten 
darf. Wenn wir dem Auftraggeber zu langsam oder zu teuer werden, müssen 
wir mit Abbruch des Projektes rechnen - natürlich ohne Bezahlung. 
Umgekehrt hat uns aber noch niemals ein Kunde abgestraft, wenn wir 
übermäßig eilig gehandelt haben.

Für das Testen und die Dokumentation gibt man uns (zusammen gerechnet) 
typischerweise 30% bis 50% der gesamten Entwicklungszeit. Was dabei 
heraus kommt, kannst du dir denken. "Notlösung" wäre dazu ein passender 
Begriff, oder "geht so gerade noch". Aber immerhin besser als gar 
nichts.

Ich hatte auch mal in einer Firma gearbeitet, wo man mit voller Absicht 
gar nicht dokumentiert hat und mangels Doku auch nicht ernsthaft testen 
konnte. Der Code sei die Doku, hieß es. Da möchte ich nicht wieder hin.

von Basti B. (basti195) Benutzerseite


Lesenswert?

Peter D. schrieb:
Automatisierte Tests testen immer genau in der
> gleichen verhersehbaren Reihenfolge und das macht es schwer, Deadlocks,
> Race Conditions usw. zu finden. Ein Ergebnis kann zwar richtig sein, das
> Programm reagiert aber falsch, wenn es zu spät vorliegt.

Das stimmt so zunächst natürlich. Hierbei handelt es sich um eine 
allgemeine Schwierigkeit von SW-Tests.
> Insbesondere äußere Ereignisse von Sensoren, gestörte Daten oder > enschliche 
Eingaben
> lassen sich schwer automatisieren.
> Dadurch werden Tests für Embedded Systeme sehr komplex.

Hier kann ich jedoch nur bedingt zustimmen. Durch das auflösen der 
Abhängigkeit eines verarbeiten Modules zu z.B. der Hardware, ist es 
möglich verschiedene Szenarien zu Testen. Wird z.B. der HAL durch Stubs 
oder Mocks ersetzt „denkt“ das getestete Modul, dass es mit Realer 
Hardware interagieren würde. Auf diese kann bereits mit Unit und 
Integrationstests viele Probleme aufgezeigt werden. So können zum 
Beispiel gestörte Daten oder Interne Fehler der HAL simuliert und 
getestet werden.
Ist es nun nicht möglich mit herkömmlichen Methoden diese Abhängigkeit 
aufzulösen, kann mein System verwendet werden.

Stefan ⛄ F. schrieb:
> Für das Testen und die Dokumentation gibt man uns (zusammen gerechnet)
> typischerweise 30% bis 50% der gesamten Entwicklungszeit. Was dabei
> heraus kommt, kannst du dir denken. "Notlösung" wäre dazu ein passender
> Begriff, oder "geht so gerade noch". Aber immerhin besser als gar
> nichts.

Testen ist natürlich ein Investment welches man nicht sieht, wenn man es 
tätigt. Im Rahmen meiner Arbeit habe ich mir die Kosten angeschaut, die 
ein Bug verursacht. Hier steigt der Faktor signifikant, je später der 
Bug gefunden wird.
Meiner Meinung nach kann hier bereits viel Zeit und Frust gespart 
werden, wenn bereits direkt beim erstellen einer Funktion die 
entsprechenden Unit-Tests erstellt werden.
Ich sage das so, weil das für mich nicht ganz intuitiv war. Aus eig 
Erfahrung aber weiß ich, dass man beim erstellen/bearbeiten einer 
Funktion/Modul manchmal nicht alle Randbedingungen so wie gewünscht 
umgesetzt werden. Ein Test ist hier eine einfach Verifikation.

Grüße

von Stefan F. (Gast)


Lesenswert?

Basti B. schrieb:
> Testen ist natürlich ein Investment welches man nicht sieht

Weswegen unsere Kunden nur wenig Geld für's Testen ausgeben. Sie sind 
eher bereit, viel Geld für schnelle Problemlösungen auszugeben.

Nun liegt es also an uns, so viel Automatisierung wie möglich im Rahmen 
der gegebenen Zeit einzubauen, damit wir es später bei der Produktpflege 
einfacher haben. Lohnen tut sich das ganze aber nur, wenn es tatsächlich 
zu einer langen Produktpflege kommt. Ob es dazu kommt, weis man wiederum 
anfangs noch nicht, was wiederum die Investitionsbereitschaft meiner 
Entwicklungsfirma beeinflusst.

> manchmal nicht alle Randbedingungen so wie gewünscht
> umgesetzt werden. Ein Test ist hier eine einfach Verifikation.

Das interpretieren der Anforderungen verschiebst du damit vom Entwickler 
der Anwendung auf den Entwickler der Tests, was in meinen Fällen stets 
die gleiche Person war. Jetzt darfst du mal raten, wer die technische 
Umsetzung für die Zukunft detailliert dokumentiert - die selbe Person.

Vernünftig testen kann man meiner Meinung nach nur, wenn die 
Testkriterien vom Auftraggeber selbst geschrieben werden. Habe ich so 
aber in 30 Jahren noch kein einziges mal erlebt.

von Peter D. (peda)


Lesenswert?

Was auch gerne beim Testen vergessen wird ist das Profiling. Programme 
wachsen und wachsen. Ob aber genügend CPU-Leistung verfügbar ist, alle 
kritischen Tasks rechtzeitig auszuführen, wird dem Zufall überlassen.
Zum Programmieren gehört daher auch, Engpässe zu erkennen und zu lösen. 
Z.B. ein Steppermotor darf nicht ruckeln, eine Grafikanzeige kann 
ruckeln.

von Basti B. (basti195) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Vernünftig testen kann man meiner Meinung nach nur, wenn die
> Testkriterien vom Auftraggeber selbst geschrieben werden. Habe ich so
> aber in 30 Jahren noch kein einziges mal erlebt.

Kommt drauf an von "welchen" Tests wir sprechen.
Annahme: Ich erhalte den Auftrag ein Modul zu entwerfen. Hier muss u.a. 
spezifiziert sein:

* welche Schnittellen gibt es

* verhalten eines Moduls bei Parameter x,y und Randbedingung z


Der Auftraggeber sollte nun die Anforderungen in der Abnahme überprüfen 
(z.B. durch Systemtests). Der Entwickler selber kann jedoch mit 
Unittests einzelne Funktionen und Module testen über die er die 
Kontrolle hat. Hier ist vor allem das überprüfen von Äquivalenzklassen 
wichtig (welcher Parameter hat welches gleiche verhalten). Hier kann zum 
Beispiel die Funktion zum schalten eines Pins mit dem Parameter int8_t 
drei solcher Äquivalenzklassen haben. An(>0), Aus(==0) und 
NichtDefiniert(<0)
Für jeder dieser Klassen können nun bis zu 2 Tests erstellt werden. 
Jeweils einer für die Grenzen der Äquivalenzklasse. D.h mit folgenden 
Parametern 127,1,0,-1,-128. So lassen sich schonnmal das verhalten einer 
Funktion Testen.

Die Interaktion von Modulen (Integrationstests) ist hier komplizierter. 
Hier kann es tatsächlich sein, dass unterschiedliche Interpretationen 
einer Anforderung zu unterschiedlichen verhalten führen kann. Was jedoch 
gemacht werden kann, gegen die vorhandene Spezifikation zu Testen. Hier 
hat zum Beispiel ein Modul zum Ansteuern einer Tür eine initDoor, 
setState und deInitDoor Funktion. Auch wenn in der Anforderung z.B. 
nicht spezifiziert ist was passiert wenn initDoor Fehlschlägt und dann 
setState aufgerufen wird kann hier die Interaktion, sowie die 
"klar/eindeutig" Definierte Schnittelle Überprüft werden (setState 
schreibt das erwartete Ergebnis auf den Pin).

System Tests sind hier nochmal deutlich komplizierter. Hier ist es ja 
das Ziel das System als Ganzes zu testen. Hier sollten Nachbarsysteme 
wie z.B. die Türansteuerungs-Elektronik durch Stubs ersetzt werden (was 
zum teil sehr viel Aufwand ist und wirklich Teuer werden kann, weshalb 
die oft Manuell durchgeführt werden). Hinzu kommt das hier die 
Anforderung oft noch schwammiger sind als auf Modul/Komponenten Ebene.

Zum Aufwand er einzelnen Test lässt sich sagen:

Unit << Integration << Systemtest

So kann das erstellen eines Unittests in wenigen Minuten erledigt sein. 
Integrations- und System -Tests sind m.m.n. zwischen 30min und vielen 
Stunden. Je nachdem wie hier die Testumgebung ist.

Natürlich kann ich hier nur aus der theoretischen Sicht sprechen, da ich 
noch nie in einem wirklich großen Projekt gearbeitet hab.

von Stefan F. (Gast)


Lesenswert?

Gut zu Testen ist auf jeden Fall um ein vielfaches komplexer, als gut zu 
programmieren.

von Michael Gugelhupf (Gast)


Lesenswert?

Peter D. schrieb:
> Automatisierte Tests testen immer genau in der
> gleichen verhersehbaren Reihenfolge

Merkwürdige Behauptung. https://de.wikipedia.org/wiki/Fuzzing

von Peter D. (peda)


Lesenswert?

Michael Gugelhupf schrieb:
> Merkwürdige Behauptung. https://de.wikipedia.org/wiki/Fuzzing

Wichtig ist besonders, daß die Daten zu zufälligen Zeiten kommen und 
auch asynchron zum CPU-Takt.
Wenn die CPU die Daten selber erzeugt, sind sie zwangsläufig schön 
regelmäßig und synchron.
Wenn z.B. 2 Signale immer nacheinander kommen müssen, können diese durch 
die Interrupthandler zu unterschiedlichen Zeiten eingelesen werden. Die 
CPU stellt also scheinbar einen verbotenen Zwischenzustand fest und 
meldet einen Fehler, obwohl alles korrekt ist.

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
Noch kein Account? Hier anmelden.