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....
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
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.
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?
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.
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.
Naja, wenn einfach drauflos programmiert wird, wenig dokumentiert und wenig systematisch vorgegangen wird, hat das oft eine triviale Ursache: Zu wenig Resourcen (Zeit).
@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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.