Thema: Delphi DUnit

Einzelnen Beitrag anzeigen

Benutzerbild von Sanchez
Sanchez

Registriert seit: 24. Apr 2003
Ort: Neumarkt Stmk
892 Beiträge
 
Delphi XE6 Enterprise
 
#2

Re: DUnit

  Alt 4. Feb 2004, 19:33
Testen von vererbten Klassen, Exceptions

Dieses Beispiel ist aus einem Java-Buch portiert.

Angenommen wir haben eine Klasse TBook und eine fertige Testklasse dazu.
Das Buch wird mit seinem Titel, dem Einkaufspreis(FWholeSale) und dem empfohlenen Preis (FRecommended) erzeugt.
Das Buch kann jetzt im Rahmen dieser beiden Preise verkauft werden. Nach dem Verkauf kann der Profit abgefragt werden.

Delphi-Quellcode:
type
  TBook = class
  private
    FName : string;
    FWholeSale, FRecommended, FSoldFor : double;
  public
    constructor Create(AName : string; AWholeSale, ARecommended : double);
    function GetName: string;
    function GetWholeSalePrice: double;
    function GetRecommendedPrice: double;
    procedure Sell(APrice : double); virtual;
    function GetSoldFor: double;
    function Profit: double; virtual;
end;
Die Testklasse sieht wie folgt aus:
Delphi-Quellcode:
type
  TBookTest = class(TTestCase)
  private
    FBook : TBook;
    FName : string;
    FWholeSale, FRecommended : double;
  protected
    procedure SetUp; override;
    procedure TearDown; override;
  public
    constructor Create(MethodName: String); override; //Hier werden FName, FWholeSale & FRecommended gesetzt

  published
    procedure testCreation;
    procedure testSellAtRecommendedPrice;
    procedure testSellAtWholeSalePrice;
    procedure testSellBelowWholeSalePrice;
    procedure testSellAboveRecommendedPrice;
end;
Der Verkaufspreis des Buches darf nicht kleiner sein als WholeSalePrice (wird durch testSellBelowWholeSalePrice geprüft) und auch nicht größer als RecommendedPrice (-->testSellAboveRecommendedPrice) ansonsten wird eine EPriceOutOfBounds-Exception geworfen.

Wir nehmen jetzt mal an, diese Klassen sind bereits geschrieben. Jetzt wollen wir ein Fixpreisbuch (TFixedPriceBook) einführen, dass sich nur zum Recommended-Price verkaufen lässt. Diese Klasse wird von TBook abgeleitet.

Die meisten Test-Prozeduren aus der BookTest-Klasse können gleich bleiben, nur ist jetzt kein Verkauf zum Einkaufspreis (Wholesaleprice) mehr möglich. Also muss die testSellAtWholeSalePrice überschrieben werden.

Bisher wurde in Setup das Buch wie folgt erzeugt:
Delphi-Quellcode:
procedure TBookTest.SetUp;
begin
  inherited;
  FBook := TBook.Create(FName, FWholesale, FRecommended);
end;
Diese Funktion kann nicht einfach ohne Aufruf von inherited in TFixedPriceBookTest Überschrieben werden. Mit Aufruf von inherited wird jedoch ein Objekt der falschen Klasse erzeugt. Also überlassen wir die Erzeugung des zu testenden Objektes einer Funktion die ohne weiteres überschrieben werden kann.

Delphi-Quellcode:
procedure TBookTest.SetUp;
begin
  inherited;
  FBook := CreateBook(FName, FWholesale, FRecommended);
end;

function TBookTest.CreateBook(AName: string; AWholeSale,
  ARecommended: double): TBook;
begin
  result := TBook.Create(AName, AWholeSale, ARecommended);
end;
Außerdem wird testSellAtWholeSalePrice aufgrund des geänderten Verhaltens zum Überschreiben markiert.
Delphi-Quellcode:
...
    procedure testSellAtWholeSalePrice; virtual;
..
Nun leiten wir die Klasse TFixedPriceBookTest von TBookTest ab.

Delphi-Quellcode:
type
  TFixedPriceBookTest = class(TBookTest)
  protected
    function CreateBook(AName: String; AWholeSale: Double;
      ARecommended: Double): TBook; override;
  published
    procedure testSellAtWholeSalePrice; override;
    procedure testSellBelowRecommendedPrice;
    procedure testProfitBeforeSale;
end;
Hier testen wir jetzt den Verkauf unter dem fixen Preis und den Profit vor dem Kauf (steht immerhin schon fest). Außerdem ist nun kein Verkauf zum Einkaufspreis mehr möglich.

Hier sehen wir schon, dass sich die Klasse TFixedPriceBook in 2 Methoden anders verhalten muss.
Delphi-Quellcode:
type
  TFixedPriceBook = class(TBook)
  public
    procedure Sell(APrice: Double); override;
    function Profit: Double; override;
end;
Der Profit kann vor dem Verkauf bereits abgefragt werden und der Verkauf ist nur noch zum RecommendedPrice möglich.

Zuerst muss einmal ein Objekt von TFixedPriceBook erzeugt werden.
Delphi-Quellcode:
function TFixedPriceBookTest.CreateBook(AName: String; AWholeSale,
  ARecommended: Double): TBook;
begin
  result := TFixedPriceBook.Create(AName, AWholeSale, ARecommended);
end;
An den fixen Daten in Create sehen wir welchen Profit wir haben

Delphi-Quellcode:
constructor TBookTest.Create(MethodName: String);
begin
  inherited;
  FName := 'Ein Testbuch';
  FRecommended := 12.0;
  FWholeSale := 10.0;
end;

procedure TFixedPriceBookTest.testProfitBeforeSale;
begin
  CheckEquals(2.0, FBook.Profit, 0.00);
end;
Anschließend versuchen wir, zum Einkaufspreis und unter dem fixierten Preis zu verkaufen. Beides muss mit einer EPriceOutOfBounds-Exception fehlschlagen.
Delphi-Quellcode:
procedure TFixedPriceBookTest.testSellAtWholeSalePrice;
begin
  try
    FBook.Sell(FWholeSale);
    Fail('EPriceOutOfBounds-Exception erwartet');
  except
    on EPriceOutOfBounds do
  end;
end;

procedure TFixedPriceBookTest.testSellBelowRecommendedPrice;
begin
  try
    FBook.Sell(FRecommended - 0.01);
    Fail('EPriceOutOfBounds-Exception erwartet');
  except
    on EPriceOutOfBounds do
  end;
end;
Tritt hier bei Sell keine EPriceOutOfBounds-Exception auf wird mit Fail eine ETestFailure-Exception ausgelöst, die den Test den Test mit der Meldung 'EPriceOutOfBounds-Exception erwartet' fehlschlagen lässt.
Werden beim Testen absichtlich Exceptions ausgelöst, empfiehlt sich das Stoppen bei Sprach-Exceptions abzuschalten. Kann sonst ziemlich nervig werden.

Jetzt fehlt noch die Implementierung von TFixedPriceBook, die die Tests erfolgreich durchlaufen lässt.

Der Verkauf sollt nur zum Recommended-Preis durchgeführt werden, ansonsten wird eine Exception geworfen.
Delphi-Quellcode:
procedure TFixedPriceBook.Sell(APrice: Double);
begin
  if APrice = FRecommended then begin
    inherited;
  end else begin
    raise EPriceOutOfBounds.Create('Preis verletzt erlaubte Grenzen');
  end;
end;
Da ein Buch den Fixpreis und den Einkaufspreis bereits im Konstruktor übergeben bekommt, kann der Profit immer abgerufen werden.
Delphi-Quellcode:
function TFixedPriceBook.Profit: Double;
begin
  Profit := FRecommended - FWholeSale;
end;
Die Tests sollten jetzt zu 100% durchlaufen.

Ich hoffe, meine Gedankengängen konnte gefolgt werden.

grüße, daniel
Daniel
Testen ist feige!
  Mit Zitat antworten Zitat