Unit-Tests in Delphi mit DUnit

Delphi enthält standardmäßig ab Version 2010 das Test-Framework DUnit. Dieses ist inspiriert vom JUnit-Framework und ermöglicht Unit-Testing auch unter Delphi. Für ältere Versionen der Delphi IDE ist das Framework auch unter http://dunit.sourceforge.net/ erhältlich.

Grundsätzlich sei gesagt, dass DUnit mittlerweile veraltet ist und nicht mehr weiter gewartet wird. Es gibt aber mit dem DUnitX-Framwork einen Nachfolger für DUnit, welcher in Delphi Versionen ab 2010 verwendet werden kann. Ältere Versionen werden allerdings nicht mehr unterstützt.

Vorerst wollen wir uns aber DUnit ansehen, da in unserer Version Delphi XE der Wizard zum Erstellen von Testklassen integriert ist. In einem weiteren Blog-Eintrag erkläre ich, wie der Umstieg auf DUnitX erfolgt.

Zuerst erstellen wir eine einfache Formularanwendung (VCL-Formularanwendung) mit einem Fenster, einem Button, ein paar Labels und SpinEdits:

Um die Addition durchzuführen, erstellen wir eine zusätzliche Unit welche eine Klasse namens TAddCalculation deklariert:

unit AddCalculator;

interface

type
TAddCalculator = class(TObject)
    public
        constructor create;
        function getResult(number1, number2: integer): integer;
    private
    end;


implementation

constructor TAddCalculator.create;
begin
end;

function TAddCalculator.getResult(number1, number2: integer): integer;
begin
    result := number1 + number2;
end;
end.

Diese Klasse wird in unserer Form verwendet um die beiden eingegebenen Zahlen zu addieren:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Spin, AddCalculator;

type
  TForm1 = class(TForm)
    Button1: TButton;
    LabelResult: TLabel;
    SpinEdit1: TSpinEdit;
    SpinEdit2: TSpinEdit;
    Label2: TLabel;
    Label3: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    addCalculator: TAddCalculator;
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
    labelResult.Caption := inttostr(addCalculator.getResult(SpinEdit1.value, SpinEdit2.value));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    addCalculator := TAddCalculator.create;
end;

end.

Soweit ein einfacher Anwendungsfall. Nun wollen wir unsere TAddCalculator-Klasse testen, ob sie die Kalkulation auch immer ordnungsgemäß durchführt. Wir wollen also einen Unit-Test erstellen. Dazu geht man in Delphi unter „Datei“ -> „Neu“ -> „Weitere“ und wählt unter dem Folder Unit-Test die Erstellung eines neuen „Testprojekt“ aus:

Wir wählen unser Quellprojekt, geben den Namen unseres Test-Projektes an und fügen das Testprojekt unserer Projektgruppe hinzu. Dies hat den Vorteil, mit dem Öffnen unserer Projektgruppe sowohl die Anwendung als auch die Tests gleichzeitig zu öffnen.

Als Test-Runner wählen wir GUI:

Nachdem unser Test-Projekt angelegt ist, wollen wir unseren ersten Test anlegen. Hierzu gehen wir wieder auf „Datei“ -> „Neu“ -> „Weitere“, wählen diesmal unter dem Ordner „Unit-Test“ die Erstellung eines „Test-Fall“ aus. Im darauf folgenden Fenster geben wir die zu testende Quelldatei an und welche Methode wir testen wollen:

Mit dem Klick auf „Fertig stellen“ erstellt Delphi ein Skelett der Testklasse, welches wir für unsere Zwecke erweitern können:

unit TestAddCalculator;
{

  Delphi DUnit-Testfall
  ----------------------
  Diese Unit enthält ein Skeleton einer Testfallklasse, das vom Experten für Testfälle erzeugt wurde.
  Ändern Sie den erzeugten Code so, dass er die Methoden korrekt einrichtet und aus der 
  getesteten Unit aufruft.

}

interface

uses
  TestFramework, AddCalculator;

type
    // Testmethoden für Klasse TAddCalculator

    TestTAddCalculator = class(TTestCase)
    strict private
        FAddCalculator: TAddCalculator;
    public
        procedure SetUp; override;
        procedure TearDown; override;
    published
        procedure TestgetResult;
    end;

implementation

procedure TestTAddCalculator.SetUp;
begin
    FAddCalculator := TAddCalculator.Create;
end;

procedure TestTAddCalculator.TearDown;
begin
    FAddCalculator.Free;
    FAddCalculator := nil;
end;

procedure TestTAddCalculator.TestgetResult;
var
    ReturnValue: Integer;
begin
    ReturnValue := FAddCalculator.getResult(2, 3);
    CheckEquals(5, ReturnValue);
end;

initialization
  // Alle Testfälle beim Testprogramm registrieren
  RegisterTest(TestTAddCalculator.Suite);
end.

Entscheidend sind die 2 Zeilen 47/48:

    ReturnValue := FAddCalculator.getResult(2, 3);
    CheckEquals(5, ReturnValue);

Hier rufen wir die Methode „getResult“ unserer zu testenden Klasse auf und überprüfen mithilfe der Funktion CheckEquals, ob das Ergebnis auch unserem erwarteten Resultat entspricht.

CheckEquals ist eine der Funktionen, die das DUnit-Framework zur Verfügung stellt. Es entspricht dem AssertEquals in Java. Daneben gibt es noch folgende Funktionen:

  • 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

Starten wir nun unseren Test, indem wir einfach auf den Ausführen-Button in Delphi klicken, erscheint ein Fenster in welchem wir die Tests starten können und das Ergebnis farblich präsentiert bekommen:

Um wieder unsere Beispielanwendung starten zu können, müssen wir nur in der Projektverwaltung ein Doppelklick auf diese durchführen, sodaß diese fett angezeigt wird. Ein weiterer Klick auf den Ausführen-Button startet nun wieder unsere Anwendung:

 

Schreibe einen Kommentar