Forum: Mikrocontroller und Digitale Elektronik Testen und Verifizierung von Code


von X. A. (wilhem)


Lesenswert?

Hallo zusammen,
ich habe hier eine Frage.
Ich weiß, dass meine Frage nicht leicht und einfach beantwortet werden 
kann, aber ich würde mich sowieso freuen, wenn ich von Euch Anregungen 
und Tipps bekommen kann.

Ich arbeite derzeit bei einer Fa., wo relativ viel Code in kürzster Zeit 
geschrieben worden ist. Die Fa. ist klein und das Team von Entwicklern 
sehr sehr klein. Ein Versionsverwaltungsprogramm wird im Team benutzt, 
um die SW-Geschichte sauber zu tracken.

Nun kommt es aber dazu, dass wir nicht nur am Code sondern auch an 
dessen Qualität arbeiten wollen. Insbesondere wollen wir sicherstellen, 
dass der Code den Mindestanforderungen an Qualität und Sicherheit 
genügt. Das Problem ist nur, dass wir eine einfachere und direkte 
Prüfmethode in unseren Workflow einfließen lassen wollen.
Ich habe lange recherchiert und lange nach einem Verfahren gesucht, um 
einiges einzuleiten. Dennoch fürchte ich, dass die meisten Methode ein 
Overkill für unser Team sein werden. Die folgende Diskussion fand ich 
auch sehr interessant: Beitrag "Software für sicherheitskritische Anwendungen"
hilft mir aber nichts weiter.

Eine Funktion in einer irgendwelchen Programmiersprache zu schreiben und 
kurz man eben auszuprobieren ist wohl kein Test. So habe ich mich 
herumgeguckt und bin auf diese Methode gestoßen:

1 - 2 Entwickler bekommen dieselbe Aufgabenstellung. Einer überlegt 
welche Funktionen implementiert werden sollen und wie man das Problem 
strukturieren kann. Der zweite schreibt den Code nach seinen 
Anweisungen. Am Ende prüft der erste den Code seines Kollegen. Die 
beiden wechseln sich bei der nächsten Aufgabenstellung ab.

Der Ansatz ist in sich sehr interessant. Ich frage mich nur, wie kann es 
funktionieren, wenn einer krank oder abwesend ist. Ich fürchte, man 
braucht mehr als 2 Entwickler für die selbe Aufgabenstellung...

2 - Angenommen, man hat einen (für den Mensch) sinnvollen Code 
geschrieben nd nun will man eine Art Test für die geschriebenen 
Funktionen durchführen. Man schreibt also eine Funktion (in welcher 
Programmiersprache?!? Dieselbe?? Compiler auch der selbe?!??), welche 
die zu prüfende Funktion testen soll. Oft es ist aber so, dass 
Funktionen, welche Treiber für ein Peripheriegerät darstellen, auch die 
HW-Funktionalitäten in Echtzeit benötigen. Das erschwert die 
Implementierung einer Prüffunktion. Bspl. einer Funktion wird eine 
Temperaturmessung übergeben, welche vom System abhängig ist und nicht 
"emuliert" werden kann.
Sollte diese Funktion direkt in dem Code geschrieben werden, in dem die 
zu prüfenden Funktionen implementiert sind? Oder gibt es dazu geeignete 
"Schnittstelle"?

3 - Richtlinien, die vorschreiben, wie man Code schreiben soll und 
welche Fehler (implizite Casts usw...) vermieden werden können, gibt es 
verschiedene. Daher bin ich nicht sicher, welche man am besten 
berücksichtigen soll.

Vielleicht klingen die Fragen dumm. Dennoch versuche ich eine 
Prüf-Testmethode, welche nicht viel zu aufwändig ist, um 
sicherzustellen, dass der Code die minimalsten Anforderungen erfüllt.
Ich denke auch in Richtung Verifizierung und Validierung von 
akkreditierten Institutionen (TÜV!?!?). Dennoch wäre dies ein viel zu 
großer Schritt und bis dahin muss ich tief atmen....

von Georg B. (diereinegier)


Lesenswert?

Die eine, kurze richtige Antwort gibt es darauf tatsächlich nicht. Es 
ist gut, wenn man im Team Extremisten hat, die sehr viele Tests haben 
möchten und andere, die lieber fast gar keine Tests wollen. Und andere, 
die dazwischen einen Kompromiss aushandeln können.

1. Das erinnert mich ein bißchen an Pair-Programming, wo man die Rollen 
in 10-15 Minutenintervallen wechselt. Auf jeden Fall lohnt Test Driven 
Development einen Blick: bevor man den Kode schreibt, schreibt man 
erstmal Tests, die dokumentieren, was man von der Implementierung 
erwartet. Das ist dann eine maschinenlesbare und hoffentlich auch 
menschenlesbare Dokumentation. Nach einer Weile sind diese Test oft die 
beste Dokumentation über die Funktionalität einer Komponente, weil sie 
aktuell gehalten werden muß, um die Test "grün" zu halten.

2. Man kann versuchen, Hardware-Abhängigkeiten unter Interfaces (in C++ 
abstrakte Basisklassen) zu verstecken. Logischer Kode sollte nur gegen 
diese Interfaces programmiert werden. Im Test kann man dann diese 
Interfaces durch Fakes ersetzen. Dazu gibt es Mocking-Frameworks. Für 
zeitkritischen Kode geht das sogar auch, der Fake-Kode kann z.B. die 
Zeiten zwischen Aufrufen messen.
(Die Test-Päpste können Dir genau erklären, was der Unterschied zwischen 
Fake, Stub und Mockup ist...)

3. Solche Regeln erscheinen mir immer etwas kleinkariert. Wer in C
1
while(*dst++=*src++);
 schreibt, der benutzt ein gängiges Idiom und sollte nicht dazu gebracht 
werden, "anfängerfreundlich" zu schreiben. Wer aber mehrzeilige 
Rechenausdrücke mit der minimal möglichen Zahl von Klammerungen ohne 
temporäre Variablen verwendet, der sollte vielleicht in's Gebet genommen 
werden.

Ich habe Continuous Integration Builds und automatisierte Tests mit 
NUnit zu schätzen gelernt, die geben bei Änderungen ein Sicherheitsnetz 
ohne das man sich manchen Umbau garnicht trauen darf. Bei Projekten 
mit Millionen Zeilen geht es kaum anders, glaube ich.

Literaturempfehlung:
The Art of Unit-Testing.

: Bearbeitet durch User
von X. A. (wilhem)


Lesenswert?

Hi Georg,
vielen Dank für Deine ausführliche Antwort.
Ich werde demnächst das Buch in einer Buchhandlung anschauen. Ich war 
bereits auf "Clean Code" von Robert Martin gestoßen, um nur 
festzustellen, ich muss viel in diesem Bereich (Qualität und Tests) viel 
viel lernen.

Damit ich langsam verstehen kann: Du sagt, man schreibt den Test und 
dessen Funktionen noch ehe man den Code implementiert. Wie geht es aber 
in der Praxis?! Man fügt eine Funktion in seinem Quellcode, die die zu 
überprüfende Routine testen?!? Wie soll ich es mir am besten 
vorstellen?!

Das mit dem Mocking hatte ich gerade in dem Buch "Der C++ Programmierer" 
auch gefunden. Es scheint eine ganze Menge an Framework da verfügbar zu 
sein.

von X. A. (wilhem)


Lesenswert?

Hallo Leute,
habt Ihr ein Beispiel oder ein Dokument, das erklärt, wie man mit dem 
Unittest von Software (in diesem Fall C/C++ - Funktionen) vorgeht??

Womit soll man anfangen?

von Heiner K. (heinerkuhlmann)


Lesenswert?

Hi Dave,

Du bist nicht der einzige, der vor diesen Problemen steht.

Zunächst einmal: Euch helfen keine umfangreichen Prozesse und Methoden, 
da Ihr bereits Eure Vorgehensweise praktiziert.

Ich empfehle, Inspektionen einzuführen und Eure Vorgehensweise sanft zu 
ändern.

1. Definieren, was das System, das Programmteil leisten soll 
(Spezifikation).

Beschreibt kein Pille-Palle wie eine C-Funktion sondern etwas, was 
mindestens einen Mann-Tag zur Codierung braucht. Umfangreiche System 
werden später in Subsysteme zerlegt. Eine kleine Einheit wäre z.B eine 
Klasse, ein System eine Mäusemelkmaschine.

Schreibt auf maximal einer DIN A4 Seite per Hand, WAS das System bzw. 
Programm leisten soll, als Black-Box. WAS kommt herein (Bedingungen, 
Werte-Bereiche, Zeiten, Folgen) und WAS soll herauskommen 
(Werte-Bereiche, Zeiten, Folgen). Es sollte so beschrieben sein, dass 
man anhand dessen prüfen könnte, ob des System die Definition erfüllt. 
Derart vorgehen, wie es Elektronik-Ingenieure für Ihre Elektronik 
machen. Details, wie Protokolle usw. werden separat beschrieben. Ein 
(einziges) Hand-Gemälde ist sehr hilfreich. Es kann auf einem weiteren 
Blatt erstellt werden und sollte aber nicht mehr als 10 Knoten 
enthalten.

Wichtig ist, zu beschreiben, WAS geleistet werden soll. Das WIE hat hier 
nichts zu suchen.

Bei einer Klasse wird z.B. die Wirkung von Methoden und deren 
Zusammenspiel beschrieben. Bei einer Mäusemelkmaschine, wie viele Mäuse 
pro Stunde gemolken werden sollen, ob die Milch separat oder insgesamt 
gesammelt wird, auf welche Temperatur die Milch gekühlt wird ....

Eine Seite für die Beschreibung ist hart. Sie kostet Gehirschmalz und 
zwingt, auf das Wesentliche einzugehen.

Wenn die Aufgaben des Systems zu komplex sind, um auf einer Seite 
beschrieben werden zu können, alles überdenken. Das Ganze mit 
Subsystemen beschreiben. Diese Subsysteme werden dann später genauso 
erstellt.
Die Pumpe, der Milchbehälter, das Bett für die Mäuse, die Steuerung ...

Erst, wenn diese Spezifikation steht, kann sie in die EDV gebracht 
werden. Oft reicht ein Scan.

Diese Beschreibung kostet Zeit, aber sie wird im weiteren Vorgehen glatt 
wieder eingespart. Meistens muss sie der Entwickler sowieso leisten - im 
Kopf, für andere unsichtbar und dem Vergessen anheim gestellt.

2. Parallele Arbeit

Mehrere Personen sind beteiligt: Codierer, Testentwickler, Tester aber 
auch Anwender des Systems, Hardware-Menschen usw. Anwender ist auch 
derjenige, der den Code in seinem Programm verwendet, Daten von dem 
System erhält.

2.1 Kodieren des Systems.

Es wird genau das entwickelt, was unter 1. spezifiziert wurde. Rüschen 
am Bett der Mäuse sind überflüssig. Debugging ist kein Test sondern 
selbstverständliches Handwerkzeug.

2.2 Testablauf und -Umgebung erstellen

Entwickeln einer Umgebung, die das unter 1. definierte System testet. 
Das können Programme, Hardware und oder Menschen sein. Dazu gehört auch 
eine Beschreibung, wie getestet wird (Kurz und bündig). Das Subsystem 
wird meistens nicht in seiner späteren Umgebung getestet sonder in einer 
eigenen Testumgebung. Es darf alles verwendet werden, was hilft, 
Hardware, beliebige Systeme, Sprachen, den DAU ...

Manchmal ist es erforderlich, Testpunkte in den Code einzubauen. Bei OO 
ist es eine simple Testmethode, die ein bestimmtes Datum liefert. 
Spezifikation anpassen und Auftrag an den Codierer.

Meistens ist es nicht hilfreich, dem Codierer die Testumgebung zu geben. 
Wenn diese fehlerhaft ist, fällt der Codierer leicht darauf herein. 
Andererseits sollte der Testentwickler den Code nicht kennen.

3. Test

Der Test wird von einer dritten Person durchgeführt. Sie geht eiskalt 
nach der Testbeschreibung vor und sammelt:
Das ist gut, das ist schlecht.
Keine Fehlersuche, keine Beurteilung.
Rücksprache mit dem Entwickler lenken nur ab kosten viel Zeit.

4. Ein Team entscheidet nach Abschluss des Tests, ob man mit dem 
Ergebnis leben kann oder Nacharbeiten nötig sind. Es wird auch hier 
nicht die Ursache untersucht.

5. Nacharbeiten

Was ist los? Ist der Test fehlerhaft oder das System oder gar die 
Definition? Keine Hemmungen dort anzupassen, wo es hapert.

Die obigen Arbeiten werden kurz und knackig erledigt. Kein grosses 
Palaver, schnelle pragmatische Entscheidungen. Eigentlich so, wie man in 
kleinen Teams arbeitet, aber mit klaren Abläufen, Strategien und Regeln. 
Jeder springt für jeden ein, alle sind gemeinsam verantwortlich. Wir 
kennen das vom Fussball.

Vor allem sollten Inspektionen einführt werden.

Das ist eigentlich nichts anderes, als dass eine oder mehrere Personen 
die Spezifikation, den Code, die Testbeschreibung lesen und - das ist 
das wichtigste - nach bestimmten Checklisten prüfen. Es wird nichts 
bemängelt, sondern nur auf Dinge hingewiesen, die problematisch sein 
könnten.
Anmerkungen wie: "das ist aber nicht schön ..." sind nicht erlaubt. Aber 
"das verstehe ich nicht" ist ausdrücklich erwünscht. Es können mehrere 
Inspektoren beteiligt sein, z.B Spezialisten, die nur auf einen Teil der 
Checklisten achten. Prinzip: kurz und knackig.

Nach dem Prüfen treffen sich die Beteiligten (Inspektor, Codierer oder 
...) und entscheiden, was bearbeitet werden muss. Im Abschluss passen 
sie Checklisten an: Neue Regeln aufgrund der Befunde, Anpassungen, 
Streichungen. Wieder kurz und knackig.

Checklisten? Welche Checklisten? Wie anfangen?

Nun normalerweise hat man eine mehr oder weniger vage Vorstellung, wie 
etwas aussehen sollte oder wo Probleme liegen. Das ist die erste 
Checkliste.

Wenn man so will, ist oder wird eine Checkliste eine Liste von Design-, 
Codier- und Testregeln. Meine Empfehlung ist, sie sollten so rigide 
sein, dass den z.B. Codierern die Tränen kommen.

   while(*dst++=*src++);

ist ganz bestimmt nicht drin. Aber es sind Regeln, gegen die verstossen 
werden kann kann. Der Inspektor wird den Verstoss kennzeichnen und 
Verstösse können später bewilligt werden. Die Checklisten werden immer 
wieder angepasst, gestrichen, erweitert, geändert.

Inspektionen sind wegen der Checklisten sehr effizient (Zeit, Erfolg, 
usw.). Pair-Programming, Peer-Reviews und Ödeldödel kommen da nicht mit.

Hardware

Überall wo Hardware betroffen ist, werden Hardware-Menschen bei der 
Inspektion beteiligt.

Wenn Hardware nicht verfügbar ist, helfen beim Test 
Software-Emulationen.

Bei Echtzeitsystemen ist das Testen allemal problematisch. Echte 
Timing-Probleme und Deadlocks treten so selten auf, dass sie bei Tests 
nicht erfasst werden. Hier hilft nur eine intensive Inspektion von 
Spezialisten.

Es ist klar, meistens hat man in Teams nicht Personal für alle Rollen. 
Genau das ist gut. Jeder übernimmt in verschiedenen Kontexten 
verschiedene Rollen. Ein Autor ist nie der eigene Inspektor. Der 
Codierer ist weder Testautor noch Tester. Jeder lernt die Nöte einer 
Rolle am eigenen Leibe. UND er stellt fest, welchen Sinn und Folgen die 
Regeln der Checklisten haben. Entscheidungen werden im Team getroffen.

Wie anfangen?

Was tut am meisten weh? Hier Checklisten und Inspektionen einführen.

Und noch einmal Fussball:
Die Checklisten sind die Regeln, die Checker die Linienrichter, das 
Bewertungsteam der Schiedsrichter, die Anwender die Zuschauer.
Leider spielen viele Software-Mannschaft in der 3. Kreisklasse: Regeln 
sind mangelhaft bekannt, von Strategien ganz zu schweigen.

Vielleicht hilft's.

P.S.
Oft ist professionelle Hilfe angesagt. Es kostet weniger als eigene 
Versuche.

von WaddeHaddeDuDeDa (Gast)


Lesenswert?

Dave A. schrieb:
> habt Ihr ein Beispiel oder ein Dokument, das erklärt, wie man mit dem
> Unittest von Software (in diesem Fall C/C++ - Funktionen) vorgeht??

https://www.amazon.de/Driven-Development-Embedded-Pragmatic-Programmers/dp/193435662X

Ein gutes Buch zum Einstieg in TDD im Embedded Bereich. Dort werden die 
Unit-Test-Frameworks Unity (C only) und CppUTest (C++/C) benutzt. Auf 
der GitHub Seite des Autors (https://github.com/jwgrenning) findest du 
u.a. die Beispiele aus dem Buch.

von Leidgeplagter (Gast)


Lesenswert?

Naja, wenn einfach drauflos programmiert wird, wenig dokumentiert und 
wenig systematisch vorgegangen wird, hat das oft eine triviale Ursache:

Zu wenig Resourcen (Zeit).

von Flo (Gast)


Lesenswert?

@Heiner Kuhlmann

Danke von einem unbeteiligten für diesen Text. Interessant für 
außenstehende.

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.