Philosophie der Testautomatisierung

Bei der Testautomatisierung gibt es ein paar Dinge, die man beachten sollte. Allerdings ist das Schreiben von Tests etwas ``lockerer'' als das Schreiben von Produktivcode.

Generell gilt, dass ein Test besser ist als gar keiner. Man kann also mit einem kleinen Satz an Testskripten starten und dann immer weiter aufbauen. Es sollte für jeden gefundenen Bug ein neuer Test geschrieben werden, der auch überprüft, dass dieser Bug tatsächlich behoben wurde. Damit stellt man auch gleichzeitig sicher, dass dieser Bug nicht mehr im Produktivcode auftaucht. Wenn es tatsächlich ein Bug war, sollte der neue Code des Moduls die neuen Tests bestehen, aber die alten Tests nicht. Wenn der neue Code des Moduls die alten Tests besteht, hat man den Bug nicht behoben.

In den folgenden Abschnitten werden noch ein paar Dinge genannt, die man bei der Erstellung einer Testsuite beachten sollte. Dies ist kein Muss, aber es hilft, die Tests auch richtig zu schreiben.

Testfälle generieren

Zu diesem Punkt sollte man gleich sagen, dass dies nicht immer möglich ist. In einigen Fällen muss man auf bestimmte Testfälle zurückgreifen und kann keine Testfälle automatisch generieren. Doch wo es geht, sollten die Testfälle generiert werden, um ein größeres Spektrum zu testen.

Als Beispiel soll hier mal folgendes Modul dienen:
   1 package TestPackage;
   2
   3 use strict;
   4 use warnings;
   5 
   6 sub summe{
   7     return 12;
   8 }
   9
     10 1;
In diesem einfachen Beispiel sieht man sofort, dass in der Funktion summe nicht wirklich eine Summe gebildet wird. Aber nicht alle Module sind so übersichtlich und Fehler fallen sofort auf. Manchmal sind es hunderte Zeilen von Code, die einen Fehler verursachen können.

Nun aber zurück zu dem Thema, warum Testfälle generiert werden sollen. Wenn nur hartcodierte Testfälle berücksichtigt werden, können Tests erfolgreich sein, obwohl das Modul eigentlich fehlerhaft ist.

Das folgende Testskript läuft ohne Fehler durch, obwohl das oben gezeigte Modul nicht korrekt arbeitet:
   1 #!/usr/bin/perl
   2
   3 use strict;
   4 use warnings;
   5 use TestPackage;
   6 use Test::More tests => 4;
   7
   8 my @testvalues = ([1,11],[2,10]);
   9 for my $ref (@testvalues){
     10     is(TestPackage::summe(@$ref),12);
     11 }
     12
     13 my @test2      = ([6,6],[5,7]);
     14 for my $arref (@test2){
     15     my ($sum1,$sum2) = @$arref;
     16     is($sum1 + $sum2 - TestPackage::summe(@$arref),0);
     17 }
Die Ausgabe ist absolut super! 0 Fehler! Das Modul kann ja nur korrekt arbeiten.
  C:\programs>perl testskript.pl
  1..4
  ok 1
  ok 2
  ok 3
  ok 4
Wenn man hier Testfälle generiert, dann fällt schnell auf, dass das Modul vielleicht doch nicht so korrekt arbeitet.

Das Testskript angepasst sieht dann so aus:
   1 #!/usr/bin/perl
   2
   3 use strict;
   4 use warnings;
   5 use TestPackage;
   6 use Test::More tests => 5;
   7 
   8 for my $counter (0..4){
   9     my ($sum1,$sum2) = (int rand 100, int rand 40);
     10     is(TestPackage::summe($sum1,$sum2),$sum1 + $sum2);
     11 }
und schon sieht das Testergebnis gar nicht mehr so toll aus (Werte können variieren):
  C:\programs>perl testskript.pl
  1..5
  not ok 1
  #   Failed test in testskript.pl at line 10.
  #          got: '12'
  #     expected: '55'
  not ok 2
  #   Failed test in testskript.pl at line 10.
  #          got: '12'
  #     expected: '100'
  not ok 3
  #   Failed test in testskript.pl at line 10.
  #          got: '12'
  #     expected: '22'
  not ok 4
  #   Failed test in testskript.pl at line 10.
  #          got: '12'
  #     expected: '98'
  not ok 5
  #   Failed test in testskript.pl at line 10.
  #          got: '12'
  #     expected: '62'
  # Looks like you failed 5 tests of 5.
Wenn das Modul dann korrigiert ist
   1 package TestPackage;
   2 
   3 use strict;
   4 use warnings;
   5
   6 sub summe{
   7     my ($sum1,$sum2) = @_;
   8     return $sum1 + $sum2;
   9 }
     10
     11 1;
dann läuft auch der Test durch:
  C:\programs>perl testskript.pl
  1..5
  ok 1
  ok 2
  ok 3
  ok 4
  ok 5

False Dilemma

Unter dem ``False Dilemma'' versteht man den Fall, dass fehlerhafter Code mit einem falschen Test zu einem scheinbaren Erfolg führt.

Wenn ein Skript zum Beispiel das Quadrat einer Zahl berechnen soll und der Code so aussieht
   1 package Quadrat;
   2
   3 sub quadrat{
   4     my ($zahl) = @_;
   5     return $zahl + $zahl;
   6 }
Wenn ich einen Test schreibe, der so aussieht:
   1 #!/usr/bin/perl
   2
   3 use strict;
   4 use warnings;
   5 use Test::More tests => 2;
   6
   7 use_ok("Test::More");
   8 is(Quadrat::quadrat(2),4);
dann läuft der Test durch. Ist ja auch scheinbar richtig, da 4 das Quadrat von 2 ist.

Aus diesem Grund sollte man viele Tests machen.

Testreihenfolge

Bei Tests sollte man auch einen Blick auf die Reihenfolge der Tests werfen. Tests sollten immer wieder in anderen Reihenfolgen durchgeführt werden. Auch Tests, die eigentlich andere Tests vorher benötigen, die Variablen setzen, sollten durchgemischt werden. Dann so kann man überprüfen, ob das Programm auch wirklich die fehlenden Variablen anmeckert. Durch eine feste Testreihenfolge können Testergebnisse verfälscht werden.

Als Beispiel dient das folgende Programm, das zwei Zahlen, die nacheinander eingegeben werden, auf ``Perfektheit'' überprüft:
   1 #!/usr/bin/perl
   2 use strict;
   3 use warnings;
   4
   5 my $tmp;
   6
   7 for(0..1){
   8     print 'Zahl eingeben: ';
   9     my $number = <STDIN>;
     10    
     11     for my $f(1..$number-1){
     12         next unless(($number/$f) == int($number/$f));
     13         $tmp += $f;
     14     }
     15    
     16     print "Perfekte Zahl ? ", $tmp == $number ? "Ja" : "Nein";
     17     print "\n";
     18 }
Wenn erst die 6 und dann die 8 eingeben wird, erhält man folgendes Ergebnis:
  ~/entwicklung 296> perl cgi_test.pl           
  Zahl eingeben: 6
  Perfekte Zahl ? Ja
  Zahl eingeben: 8
  Perfekte Zahl ? Nein
Sieht ja echt gut aus. Mein Programm funktioniert - zumindest scheint es zu funktionieren. Tauscht man die Zahlen, bekommt man folgendes Ergebnis:
  ~/entwicklung 298> perl cgi_test.pl
  Zahl eingeben: 8
  Perfekte Zahl ? Nein
  Zahl eingeben: 6
  Perfekte Zahl ? Nein
Uuups, funktioniert doch nicht. Dieses kleine Beispiel soll zeigen, dass unterschiedliche Testreihenfolgen sehr wichtig sein können.

-- ReneeBaecker - 15 Apr 2009
Topic revision: 2009-04-15, ReneeBaecker
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um WebBottomBarExample">Rückmeldung.