Erschienen in DESIGN&VERIFICATION 0/2000, S. 66
Vorwort Obwohl der Testaufwand in der Regel den für die Codierung bei weitem übersteigt, ist sich der Softwareentwickler trotz aller Verifikationsbemühungen häufig nicht sicher, ob das Programm ausreichend getestet ist. Dass es auch anders gehen kann, zeigt dieser Beitrag.
1995 hat Willert Software Tools eine Kundenumfrage durchgeführt, in der nach dem eingesetzten Zeitaufwand für die Codierung im Vergleich zum Aufwand für Test und Fehlerbeseitigung gefragt wurde. Das Verhältnis lag bei 60% für die Codierung und 40% für den Test. 1998 wiederholte das Unternehmen die Umfrage: In der Zwischenzeit hatte sich das Verhältnis umgedreht: 40% für die Codierung und 60% für den Test. Inzwischen hat der erste Kunden gemeldet, der 80% des Aufwands für den Test und 20% für die Codierung aufbringt. Die Umfrage zeigte auch, dass sich die Ingenieure in der Regel nicht sicher sind, ob genügend Tests durchgeführt wurden. Als Abbruchkriterium für Tests nannten viele den Zeitpunkt, an dem das Produkt ausgeliefert werden muss. Eines lässt sich mit Sicherheit sagen, der Bereich Test ist mindestens genauso beachtenswert wie die eigentliche Software Erstellung.
Wenn Sie 50% oder mehr in den Test stecken, ist es an der Zeit, etwas zu tun!
Die schlechte Nachricht:
Eines ist bekannt: Qualität lässt sich nicht nachträglich in ein Produkt hineintesten. Test dient dazu, möglichst viele Fehler zu finden und anschließend zu beseitigen. Grundsätzlich gilt dabei, dass mit einem bestimmten Testaufwand ein bestimmter Prozentsatz der Fehler auffindbar ist. Wobei mit geringem Testaufwand zu Anfang relativ viele Fehler gefunden werden, und für die verbliebenen Fehler immer mehr Aufwand nötig wird. Alle Fehler zu finden, ist bei heutiger Standardsoftware unmöglich, der Aufwand ist unendlich groß.
Beispiel:
Sie haben 100 Fehler in Ihrem System. Mit einem bestimmten Testaufwand finden Sie 50% der Fehler, also 50 Fehler. Haben Sie in der selben Applikation 1000 Fehler, werden Sie mit geringfügig erhöhtem Testaufwand wiederum 50% der Fehler, also 500 finden. Das heißt aber, dass noch 500 restliche Fehler vorhanden sind. Um im zweiten Fall auf eine Restfehlerrate von 50 zu kommen, muss nahezu der 10fache Aufwand an Test aufgewendet werden.
Die gute Nachricht:
Dieser Aspekt kann auch positiv ausgenutzt werden. Es gibt verschiedene Fehlerklassen. Diese verschiedenen Klassen lassen sich durch unterschiedliche Maßnahmen verhindern oder finden. Dabei sind alle Maßnahmen voneinander unabhängig in ihrer Wirkung einsetzbar. Das heißt in jeder Klasse können mit wenig Aufwand die ersten 30% aller Fehler gefunden werden. Wenden Sie also drei verschiedene Methoden an, können Sie mit relativ wenig Aufwand 30% der Fehler jeder Klasse finden. Natürlich überschneiden sich Fehler und deshalb finden Sie nicht 90%, sondern erfahrungsgemäß vielleicht 60% aller Fehler. Würden Sie den gleichen Aufwand in nur eine Methode stecken, würde die Rate an gefunden Fehlern deutlich darunter liegen, schätzungsweise bei 40 bis 50%. Welche möglichen Methoden gibt es nun, um möglichst viele Fehler zu vermeiden und zu finden?
Weniger Fehler durch gute Struktur der Software
An erster Stelle steht selbstverständlich ein sauberes Design. Sicher erzähle ich Ihnen nichts Neues, aber wie optimieren Sie mitten im Projekt schnell einmal Ihr Design? Gar nicht. Grundsätzlich jedoch hat das Design einen sehr großen Einfluss auf die Qualität der entwickelten Software, da sind wir uns sicher einig.
Weniger Fehler durch ‚defense programming‘
Gleich an zweiter Stelle steht das ‚defence programming‘. Gerade die in Embedded-Projekten verbreitete Hochsprache C hat einen hohen Freiheitsgrad. Das kommt der Qualität von Software nicht gerade entgegen. An dieser Stelle heißt es, sich selber einschränken (defence programming). Es werden nur die C-Konstrukte benutzt, von denen keine Probleme zu erwarten sind. Ebenso verhält es sich mit der Definition von C. ANSI definiert viel und es bleiben noch mehr undefinierte Möglichkeiten, C zu verwenden. Vermeiden Sie diese Bereiche wie die Pest, denn in diesen Bereichen liegt gleich zwei Fehlerquellen:
· Es ist nicht klar, wie die einzelnen Compiler reagieren und ob sie grundsätzlich so reagieren. Dadurch können sich leicht Fehler einschleichen. · Die Hersteller von Compilern können nicht alle möglichen Verwendungsarten von C testen. Häufig werden Compiler durch Plum&Hall-Testroutinen getestet. Diese Routinen testen aber nur Konstrukte, die nach ANSI definiert sind. Der ganze Bereich, der nicht durch ANSI definiert ist, ist also schlecht bis gar nicht getestet, und dementsprechend sind hier überdurchschnittlich viele Fehler bei den Compilern zu erwarten. Dazu kommt, dass sich die Codegenerierung bei Änderungen am Compiler gleich mit ändern kann. Das bringt also gleich doppeltes Fehlerrisiko.
Fehler, die trotz der Verwendung der ‚defence programming‘-Technik auftreten, können sehr leicht mit statischen Analysern verhindert und gefunden werden. Statische Analyser parsen den C-Code und zeigen alle Code-Sequenzen, die fehleranfällig sind und geben Hinweise auf mögliche Fehlerquellen. Diese Analyser können noch einen Schritt weiter gehen und tatsächlich Fehler finden. Das geht in etwa der Geschwindigkeit mit der Compiler den Code übersetzen. Dem Anwender bleibt noch die Arbeit die Meldungen durchzugehen und evtl. den Code zu ändern bzw. mögliche Fehler zu überprüfen. Der dafür notwendige Aufwand hängt vom Programmierstil ab und kann bei schlechtem Stil erheblich sein. Trotzdem lohnt es sich hier Arbeit zu investieren, da Sie direkt die Qualität Ihres Codes verbessern und gleichzeitig Ihren Programmierstil optimieren. Im Laufe der Zeit (einige Monate) werden die Listen mit Meldungen der Analyser immer weniger und sie erreichen einen Zustand, in dem Sie im Sinne des defense programming optimal arbeiten, ohne Mehraufwand zu haben. Statische Analyser liegen in Form von Tools vor. Einfache Hinweise geben aber oft schon die Compiler, wobei es bei Embedded-Compilern oft sehr wenig Informationen gibt. Erst langsam wird dieser Bereich von den Compiler-Herstellern erkannt, zum Beispiel durch die Unterstützung des MISRA-Standards durch einige Produkte der Firma Tasking (Safer C). Der MISRA-Standard ist ein Programmierstandard, der von der Automotiv-Industrie erarbeitet wurde, um die C-Programmierung sicherer zu machen. Es gibt auch preiswerte Statische Analyser wie zum Beispiel ‚PC Lint‘. Der Preis beträgt für einen Arbeitsplatz etwa 400 DM und ich garantiere Ihnen, die Investition hat sich nach wenigen Einsätzen amortisiert.
Weniger Fehler durch Systematik beim dynamischen Test
Als letztes bleiben die restlichen Fehler, die durch gute Struktur der Software, und defense programming nicht verhindert werden konnten. Diese Fehler werden mit Hilfe von dynamischer Analyse gesucht. Das ist die aufwendigste Form der Fehlersuche und aus diesem Grund sollte sie als letztes angewandt werden. Trotz hohem Aufwand gibt es keine Garantie, Fehler zu finden, schon gar nicht alle Fehler. Aus diesem Grund ist es wichtig, dass vorher alle Möglichkeiten Fehler zu vermeiden ausgeschöpft wurden. Kennen Sie die 80/20 Regel? Bezüglich der Qualität von Software besagt sie, dass 80% der Fehler in 20% der Software auftreten. Dazu ist noch zu sagen, dass sich die Anzahl der Fehler direkt proportional zur Komplexität verhält. Hohe Komplexität bedeutet auch viele Fehler. Mit diesem Wissen im Hintergrund können nun auch die dynamischen Tests sehr viel effizienter durchgeführt werden. Schaffen Sie sich eine Möglichkeit, die Komplexität Ihrer Module und Funktionen zu messen, z.B. mit dem Maß von McCabe und verlagern Sie Ihren Haupt-Testaufwand auf die 40% der komplexesten Module und Funktionen
Beispiel:
Seit z.B. ein Willert-Kunde dieses Prinzip angewandt hat, werden dort die 30% der Module mit der geringsten Komplexität nicht mehr getestet. Die Wahrscheinlichkeit in diesen Codebereichen Fehler zu finden, ist so gering, dass sich der Testaufwand nicht lohnt. (So glaubte man jedenfalls vor einigen Jahren. Inzwischen wurde herausgefunden, dass die Fehlerrate in sehr einfachen Modulen wieder ansteigen kann. Also doch lieber ein bisschen testen.) Diese gewonnene Zeit wird zusätzlich in den Test der 10% komplexesten Module gesteckt. Mit dieser Maßnahme konnte die Rate der gefundenen Fehler wesentlich erhöht werden.
Wenn Sie ‚Configuration Management‘ einsetzen, können Sie anstelle der Komplexität auch die Revision-Nummer als Hinweis auf die Fehlerwahrscheinlichkeit verwenden. Sie gibt einen guten Anhaltspunkt, wo ein Test erforderlich ist. Codebereiche (Module bzw. Funktionen), in denen viele Änderungen vorgenommen worden sind, enthalten mit hoher Wahrscheinlichkeit verhältnismäßig mehr Fehler. Module mit wenig Änderungen sind dagegen eher unkritisch. Auch im Bereich Statische Analyse ist es möglich, Testabläufe zu automatisieren. Sogenannte Regression-Tests. Grundsätzlich ist der Aufwand für die Erstellung eines automatischen Testablaufes annähernd so groß wie der Aufwand für die eigentliche Codierung. Dieser Aufwand ist natürlich nicht unerheblich und wird daher oft gescheut. Aber auch hier gilt, bei der Konzentration auf die 20% der komplexesten Module oder eventuell sogar nur auf die 10% der komplexesten Module ist der Aufwand vertretbar. Gleichzeitig ist der Nutzen bei diesem Modulen sehr groß. Zum einen ist die Änderungsquote in diesen Modulen am größten, das heißt auch die Testhäufigkeit ist sehr hoch. Zum anderen ist die potentielle Fehlerhäufigkeit in diesen Modulen natürlich hoch. Es lohnt sich auf jeden Fall über die Einführung von Regressions-Test unter diesem Gesichtspunkt nachzudenken.
Weniger Fehler durch ‚Code Reviews‘
Wenn nun die problematischen Module gemäß den oben beschriebenen Kriterien identifiziert sind, lässt sich noch eine der effizientesten Möglichkeiten Fehler zu finden nutzen. Leider ist diese Methode auch eine der unbeliebtesten bei den Entwicklern. Woran es auch immer liegt, Code Reviews werden immer nur dort eingesetzt, wo sie aus Sicherheit vorgeschrieben sind. Code Reviews gehören in den Bereich der Statischen Analyse und es gibt viele Theorien darüber. Ich möchte Sie nicht mit viel Theorie belasten, deshalb an dieser Stelle eine sehr vereinfachte Beschreibung frei nach Andreas Willert. Wenn Sie wieder einmal eine Änderung an einem Ihrer Problemmodule durchgeführt haben, gehen Sie mit dem Source zu einem Kollegen und lassen Sie sich Ihre Änderung revers erklären. Das heißt, Ihr Kollege hat nichts weiter als den C Code und analysiert nun, was das Programm macht. Das beschreibt er Ihnen und Sie vergleichen, ob das auch das ist, was Sie mit der Änderung funktional erreichen wollten. Sie werden sich wundern, wie häufig in wenigen Minuten Fehler gefunden werden. Ein weiterer Vorteil: bei Code Reviews werden Fehler nahezu aller Klassen gefunden, und das mit großer Effizienz. Aber auch hier gilt wieder: Nicht alle Fehler können mit einer Methode gefunden werden, erst die sinnvolle Kombination von mehreren Methoden bringt die Effizienz. Sie ernähren sich ja auch nicht nur von Äpfeln, auch wenn Sie wissen, das Äpfel sehr gesund sind.
Drei Sofortmaßnahmen um die Qualität Ihrer Software um 20% zu erhöhen
Wenn Sie die folgenden drei Maßnahmen für drei Monate konsequent durchführen, werden Sie die Qualität Ihrer Software garantiert wesentlich verbessern. Diese Vorgehensweise ist mit mehreren Willert-Kunden durchgeführt und in einigen Fällen bewertet worden. Qualitätsverbesserungen von 20% bei gleichem Testaufwand sind leicht zu erreichen. Lediglich die Einführung der Methoden und Tools kostet Sie einige Tage zusätzlichen Aufwand.
Erstens: · Schaffen Sie sich einen Statischen Analyser an, der den C-Source analysiert, z.B. PC-Lint. · Setzen Sie diesen Analyser konsequent ein. · Lassen Sie sich nicht von den Anfangs vielen Meldungen frustrieren. Sie merken in wenigen Wochen schrumpft die Anzahl der Meldungen auf ein erträgliches Maß und nach einigen Monaten programmieren Sie wie heute ohne jeglichen Mehraufwand.
Zweitens: · Messen Sie die Komplexität Ihrer Software und konzentrieren Sie 50% der Testzeit auf die 20% der Software mit der größten Komplexität. (Einige Editoren z.B. die neuste Version von ‚EasyCASE‘ können die Komplexität messen. Wenn Sie Configuration Management einsetzen, können Sie auch die Revision Nummern anstelle der Komplexität benutzen.)
Drittens: · Führen Sie für die 20% der Software mit der größten Komplexität Code Reviews ein. Die eingesetzte Zeit sparen Sie beim Dynamischen Test.
|
| |
|
 |
|