2011-01-06 9 views
26

Ich frage mich, was ist die beste Methode, um Ausnahmen in Dunit zu testen. Ich kenne Method-Zeiger in Delphi nicht sehr gut. Gibt es eine Möglichkeit, Argumente an einen Methodenzeiger zu binden, so dass er ohne Argumente aufgerufen werden kann. Im Moment schreibe ich immer eine zusätzliche Methode, die das manuell "bindet". Das wird nervig, wenn der SUT viele Wurftechniken hat.CheckException akzeptiert nur 0-Parameter-Methoden; Wie kann ich testen, dass andere Methoden Ausnahmen auslösen?

// What i did before i knew abput CheckExcepion 
procedure MyTest.MyMethod_BadInput_Throws; 
var 
    res: Boolean; 
begin 
    res := false; 
    try 
     sut.MyMethod('this is bad'); 
    except 
     on e : MyExpectedException do: 
      res := true; 
    end; 
    CheckTrue(res); 
end; 

// What i do now 
procedure MyTest.MyMethodWithBadInput; 
begin 
    sut.MyMethod('this is bad'); 
end; 

procedure MyTest.MyMethod_BadInput_Throws; 
begin 
    CheckException(MyMethodWithBadInput, MyExpectedException); 
end; 

// this would be nice 
procedure MyTest.MyMethod_BadInput_Throws; 
begin 
    CheckException(
     BindArguments(sut.MyMethod, 'this is bad'), // <-- how to do this 
     MyExpectedException); 
end; 

Antwort

41

Sie können StartExpectingException verwenden, um Ihren Methodenaufruf zu umgeben).

StartExpectingException(MyException); 
MyMethod(MyParam); 
StopExpectingException(); 
+1

+1 das sieht nach der * richtigen * Möglichkeit aus, dies zu tun; Danke, dass du mir etwas Neues beigebracht hast! –

+4

Oder noch besser, legen Sie einfach die ExpectedException-Eigenschaft in Ihrem Test. –

+1

Vielleicht einen Versuch hinzufügen ... endlich hier? –

3

Ich weiß nicht, ob DUnit es noch unterstützt, aber dies ist ein perfekter Anwendungsfall für anonyme Methoden, die 2010 in Delphi eingeführt wurden Wenn DUnit es Sie leicht, die Quelle ändern nicht unterstützt, dann können Sie sich .

+0

Hört sich gut an, aber wie definiere ich eine anonyme Methode? 'CheckException' nimmt eine' TTestMethod = Prozedur des Objekts '. Mein erster Versuch 'CheckException (procedure begin raise Exception.Create ('Fehler'); Ende, Exception);' Kompiliert nicht (inkompatible Typen-> reguläre Prozedur, Methode ptr) – hansmaad

+0

@hansmaad Sie müssten die anonyme Prozedur definieren als Variable in Ihrer Testfunktion. Und Sie müssten eine CheckException zu TTestCase hinzufügen, die "reference to procedure" als Parameter hat und nicht als procedure of object. Die Antwort von @TridenT auf StartExpectingException scheint jedoch der richtige Weg zu sein. –

+0

Das ist genau das, was ich richtig gemacht habe und es funktioniert. Man muss die 'CheckException'-Methode überladen, indem man ein' TTestProc' anstelle von 'TTestMethod' verwendet. Dann können Sie ananoymous Methoden verwenden. Aber StartExpectingException scheint der Weg zu sein in Dunit, auch wenn ich es nicht mag, dass ich die Stop .. Methode aufrufen muss. – hansmaad

3

Wie bereits erwähnt, ist dies ein großartiger Ort für anonyme Methoden.

Hier ist, wie ich es mache. I „ausgeliehen“ das von Alex Ciobanu:

procedure TestTMyClass.CheckException(aExceptionType: TClassOfException; aCode: TTestCode; const aMessage: String); 
var 
    WasException: Boolean; 
begin 
    WasException := False; 
    try 
    aCode; 
    except 
    on E: Exception do 
    begin 
     if E is aExceptionType then 
     begin 
     WasException := True; 
     end; 
    end; 
    end; 
    Check(WasException, aMessage); 
end; 

es dann rufen Sie mit so etwas wie:

CheckException(ETestingException, 
      procedure begin FMyClass.RaiseTestingException end,  
      'The ETestingException exception didn''t get raised. That is impossible!'); 
+0

Ich habe eine funktionierende Version dieser Methode erstellt und sie unten veröffentlicht. Vielen Dank für die Veröffentlichung, es ist sehr nützlich für mich. – DBedrenko

1

zu verwenden StartExpectingException() ist nicht der beste Weg, wenn Sie mehr als eine Ausnahmefällen testen möchten. Um alle möglichen Fälle in meinem Testverfahren zu testen, zusammen mit Ausnahmen verwende ich diesen Algorithmus:

uses 
    Dialogs; 
procedure MyTest.MyMethod_Test; 
begin 
    // Test for Exceptions 
    try 
    MyMethod(MyParam1CreatingException1); 
    ShowMessage('Error! There should have been exception: Exxx here!'); 
    Check(false); 
    except on E: Exception do Check(E is ExceptionType1); end; // This exception is OK 
    try 
    MyMethod(MyParam2CreatingException2); 
    ShowMessage('Error! There should have been exception: Exxx here!'); 
    Check(false); 
    except on E: Exception do Check(E is ExceptionType2); end; // This exception is OK 
    // ... test other exceptions ... 

    // Test other parameters 
    CheckEquals('result1', MyMethod(MyParam1)); 
    CheckEquals('result2', MyMethod(MyParam2)); 
    // ... other tests ... 
end; 

Der Grund, warum ich ShowMessage('Error! There should be exception: Exxx here!'); anstelle der bereitgestellten Check(false, 'There should have been an EListError.'); Methode ist, dass in meinem Fall (Delphi6) die Check(boolean, 'Message') tut funktioniert nicht - es zeigt die Nachricht nicht an, falls Check innerhalb von try...except blockiert ist (weiß nicht warum).

0

Dies ist die Arbeits- und verbesserte Version von Nick Hodges' Antwort, die Unterklassen DUnit der TestFramework.TTestCase:

uses 
    TestFramework, System.SysUtils; 
type 
    TTestCode = reference to procedure; 

    TTestCasePlus = class(TestFramework.TTestCase) 
    procedure CheckException(
     ExceptionType: TClass; Code: TTestCode; const Message: String = ''); 
    end; 

implementation 

procedure TTestCasePlus.CheckException(
    ExceptionType: TClass; Code: TTestCode; const Message: String = ''); 
{ Check whether some code raises a specific exception type. 

Adapted from http://stackoverflow.com/a/5615560/797744 

Example: 

    Self.CheckException(EConvertError, 
         procedure begin UnformatTimestamp('invalidstr') end); 

@param ExceptionType: The exception class which we check if it was raised. 
@param Code: Code in the form of an anonymous method that should raise the 
    exception. 
@param Message: Output message on check failure. } 
var 
    WasRaised: Boolean; 
begin 
    WasRaised := False; 
    try 
    Code; 
    except 
    on E: Exception do 
     if E is ExceptionType then 
     WasRaised := True; 
    end; 
    Check(WasRaised, Message); 
end; 

Ein schöner Bonus dieser Methode zu überprüfen, ob eine Ausnahme über Start/StopExpectingException() erhöht ist, dass man Führen Sie den Testrouter im Debug-Build aus und es wird Sie nicht weiter belästigen mit "Exception wurde ausgelöst. Break? Continue?" Jedes Mal, wenn eine Ausnahme ausgelöst wird - obwohl es behandelt wurde.

Verwandte Themen