Organisation von Testklassenhierarchien

Jeder weiß, dass frühzeitiges und intensives Testen die Entwicklungszeit und -kosten gering hält und trotzdem: Der Softwaretest ist ein gern vernachlässigtes Kapitel der Softwareentwicklung. Es mag vielleicht daran liegen, dass viele Unit-Tests zu viel sich wiederholenden Quelltext haben. Dabei bringen die objektorientierten Programmiersprachen Konzepte mit, die man in den Tests so einsetzen kann wie in dem getesteten System (SUT, system under test).

Die Konzepte Vererbung, Polymorphie und Generizität erlauben es, klassenorientierte Testfälle zu schreiben, die dann auf alle Unterklassen der CUT (class under test) angewandt werden können. In der Literatur gibt es dazu den Begriff des symmetrischen Testtreibers („Symmetric Driver“), was nichts anderes heißt, als dass parallel zu einer Klassenhierarchie eine Testklassenhierarchie geschrieben wird. Der Name resultiert daher, dass die Testklassenhierarchie symmetrisch zur Klassenhierarchie des SUT ist. Das beigefügte Dokumente zeigt, wie die Testfallhierarchie zu einer Klassenhierarchie aussehen kann. Strukturelemente wie die Testobjekte (OUTs, OUT = object under test) sollten protected sein, sodass Unterklassen darauf zugreifen können. Die Testmethoden sind public, wie es das Testframework, z. B. JUnit, erfordert.

Illustration der allgemeinen Testklassenhierarchie

Dass Testfälle auf Unterklassenmethoden wiederholt werden, klingt vielleicht nach Redundanz, ist aber pure Notwendigkeit. Die kürzlich mit dem Turing-Preis ausgezeichnete Barbara Liskov postulierte, dass Vorbedingungen (oder auch: der Definitionsbereich) einer überschriebenen Methode nicht eingeschränkt werden dürfen. Die Nachbedingungen dürfen eingeschränkt werden, aber nicht erweitert werden. Solche Eigenschaften sollten nach Möglichkeit durch die gesamte Klassenhierarchie erhalten bleiben und eignen sich hervorragend für der „Symmetric Driver“.

Der Aufbau der Testklassenhierarchie demonstriert die Stärke objektorientierter Modellierung. Objektorientierte Softwareentwicklung hat viele Fehlerquellen beseitigt, aber es kommen auch neue hinzu, wie z. B. eingeschränkte Vorbedingungen von überschriebenen Methoden. Die Testklassenhierarchie ist hilfreich beim systematischen Aufdecken von Fehlern des objektorientierten Entwurfes.

Die Verletzung des Liskovschen Substitutionsprinzips zeigt folgendes Beispiel:

class A {
  public void method(int n) {
     if (n < 0) throw new IllegalArgumentException();
  }
}
 
class B extends A {
 public void method(int n) {
     if (n < 1) throw new IllegalArgumentException();
  }

Die folgende Testklassenhierarchie findet den Fehler, vorausgesetzt, es werden die richtigen Datenpunkte definiert.

@RunWith(Theories.class)
class TestA {
 
   @Datapoint
   public static final Integer int1 = Integer.valueOf(0);
   protected A out;
  @Before
   public void setUp() {
     out = new A();
   }
 
   @Theory
   public smokeTest(int n) {
     assumeTrue(n>=0);
     out.method(n);
   }
}
 
class TestB extends TestA {
  @Before
   public void setUp() {
      out = new B();
   }
}

Alexander Draeger

Tags: , , , , ,

  • Share/Bookmark

Kommentare sind geschlossen.