Home » Tutorials » Tools » Unit-Tests mit DUnit

Unit-Tests mit DUnit

Bedingungen überprüfen

In unseren bisherigen Tests haben wir das Ergebnis mit CheckEquals auf seine Richtigkeit geprüft. Natürlich ist es auch möglich, in einer Testmethode mehrere Dinge zu prüfen. Damit der Test erfolgreich ist, müssen alle Bedingungen erfüllt sein.
Nehmen wir den Test für GetData. Das Ergebnis ist ja unser Record, der zwei Werte enthält, die wir natürlich beide prüfen wollen. Das sieht dann so aus:

procedure TestTDemoClass.TestGetData;
var
  ReturnValue: TTestrecord;
begin
  ReturnValue := FDemoClass.GetData;
  CheckEquals('IchBinText', ReturnValue.Text);
  CheckEquals(8, ReturnValue.Number);
end;

„CheckEquals“ von DUnit ist so überladen, dass man es mit allen Standard-Datentypen verwenden kann.
Daneben gibt es aber noch weitere Methoden zum Prüfen des Ergebnisses:

  • CheckNotEquals: prüft, ob zwei Werte nicht gleich sind
  • CheckTrue: prüft, ob ein Wert true ist
  • CheckFalse: prüft, ob ein Wert false ist
  • CheckNull: prüft, ob ein Wert nil ist
  • CheckNotNull: prüft, ob ein Wert nicht nil ist
  • CheckInherits: prüft, ob eine Klasse von einer anderen abgeleitet ist
  • CheckIs: prüft, ob ein Objekt von einer bestimmten Klasse ist
  • Fail: lässt den Test einfach fehlschlagen, führt selbst keine Prüfungen durch

Und das waren noch nicht alle.

Exceptions

Interessant ist noch der Umgang mit Exceptions. Es gibt z.B. eine Implementierung, die in einem bestimmten Fall eine Exception wirft. Und der Test muss natürlich prüfen, ob die Exception auch wie erwartet ankommt.
Wir fügen in unsere zu testende Klass TDemoClass eine neue Methode namens Error ein, die einfach immer nur eine Exception wirft, wenn sie aufgerufen wird:

uses Classes;

interface

TDemoClass = class(TObject)
  public
   ...
    procedure Error();
  end;

implementation

  ...

procedure TDemoClass.Error;
begin
  raise EInvalidOperation.Create('Fehlermeldung');
end;

end.

Die Unit Classes muss eingebunden werden, damit EInvalidOperation bekannt ist.
Nun schreiben wir einen Test, der diese Methode aufruft:

type
  TestTDemoClass = class(TTestCase)
  strict private
    FDemoClass: TDemoClass;
  ...
  published
    ...
    procedure TestError;
  end;

implementation

procedure TestTDemoClass.TestError;
begin
  FDemoClass.Error;
end;

end.

Wenn wir den Test so ausführen, schlägt er fehl, weil die Exception auftritt. Der Test weiß ja nicht, dass das so sein soll:

Wir müssen dem Test also mitteilen, dass eine EInvalidOperation-Exception auftreten soll. Wenn sie das nicht tut, soll der Test fehlschlagen.

procedure TestTDemoClass.TestError;
begin
  StartExpectingException(EInvalidOperation);
  FDemoClass.Error;
  StopExpectingException;
end;

Auch in die Testklasse muss natürlich die Unit Classes eingebunden werden, damit EInvalidOperation bekannt ist.
Vor den Aufruf, bei dem die Exception erwartet wird, schreiben wir also StartExpectingException und als Parameter den Typ der Exception. Ans Ende kommt StopExpectingException. Diese Methode prüft, ob in der Zwischenzeit die gewünschte Exception aufgetreten ist und lässt ansonsten den Test fehlschlagen.
Rechnen wir hier mit einer anderen Exception als tatsächlich auftritt, schlägt der Test natürlich auch fehl. Und im DUnit-Ausführungsfenster ist die Meldung zu lesen: „unexpected exception, expected: EMyError, but was: EInvalidOperation“