2016-03-20 34 views
4

下面的代碼工作正常,calc ...生成一個異常,註釋掉或者改變calc ...不拋出異常,測試失敗。Delphi 7在StopExpectingException之後的Dunit檢查不能正常工作,因爲我期望

StartExpectingException(exception); 
    calcMembersPIPEndDate(EncodeDate(2005,01,01),true); 
    StopExpectingException('calcMembersPIPEndDate - 1st after aDay'); 

我的問題是,在此之後,我把這個測試方法放在任何檢查不執行。
所以

checkEquals(1,0); 
    StartExpectingException(exception); 
    calcMembersPIPEndDate(EncodeDate(2005,01,01),true); 
    StopExpectingException('calcMembersPIPEndDate - 1st after aDay'); 

失敗一號checkEquals

StartExpectingException(exception); 
    calcMembersPIPEndDate(EncodeDate(2005,01,01),true); 
    StopExpectingException('calcMembersPIPEndDate - 1st after aDay'); 
    checkEquals(1,0); 

通行證 - 爲什麼?

我曾試圖制定出我使用的是什麼版本的DUNIT:

testframework.pas has the following - which didn't seem to 
rcs_id: string = '#(@)$Id: TestFramework.pas,v 1.117 2006/07/19 02:45:55 
rcs_version : string = '$Revision: 1.117 $'; 
versioninfo.inc 
ReleaseNo : array[1..3] of Integer 
      = (9,2,1); 
ReleaseStr  = '9.2.1'; 
ReleaseWhen : array[1..6] of Integer 
      = (2005,09,25,17,30,00); 

回答

2

StopExpectingException不能工作,你所期望的方式。瞭解異常狀態下的執行流程以瞭解原因很重要。

考慮下面的代碼:

procedure InnerStep(ARaiseException); 
begin 
    Writeln('Begin'); 
    if ARaiseException then 
    raise Exception.Create('Watch what happens now'); 
    Writeln('End'); 
end; 

procedure OuterStep; 
begin 
    try 
    InnerStep(False); //1 
    InnerStep(True); //2 
    InnerStep(False); //3 
    except 
    //Do something because of exception 
    raise; 
    end; 
end; 

當你在上面打電話OuterStep,線//2將提高內部InnerStep異常。現在,每當產生一個異常:

  • 指令指針跳出每個方法(有點像goto)到第一的除了在調用堆棧發現最後塊。
  • Writeln('End');將不會被調用。
  • //3將不會被調用。
  • 無論中是否存在代碼OuterStep的塊接下來執行。
  • 最後當raise;被調用時,異常被重新提出,並指令指針跳到下一個除了終於塊。
  • 還要注意像提高;塊之外的任何其他異常也會跳出(有效隱藏第一個異常)。

所以,當你寫:

StartExpectingException(...); 
DoSomething(); 
StopExpectingException(...); 

有以下兩種方式:

  1. DoSomething拋出一個異常,並StopExpectingException永遠不會被調用。
  2. DoSomething不會引發異常,並且StopExpectingException被稱爲也沒有例外。

David has explained的DUNIT框架調用StopExpectingException你。但是你可能想知道如何處理你的測試用例來檢查多個異常情況。

選項1

寫出更小的測試。
你知道這就是大家都說你應該在任何情況下都這樣做嗎? :)
E.g.

procedure MyTests.TestBadCase1; 
begin 
    ExpectedException := ESomethingBadHappened; 
    DoSomething('Bad1'); 
    //Nothing to do. Exception should be raised, so any more code would 
    //be pointless. 
    //If exception is NOT raised, test will exit 'normally', and 
    //framework will fail the test when it detects that the expected 
    //exception was not raised. 
end; 

procedure MyTests.TestBadCase2; 
begin 
    ExpectedException := ESomethingBadHappened; 
    DoSomething('Bad2'); 
end; 

procedure MyTests.TestGoodCase; 
begin 
    DoSomething('Good'); 
    //Good case does not (or should not) raise an exception. 
    //So now you can check results or expected state change. 
end; 

選項2

當大衛曾建議,你可以寫自己的異常測試內部處理。但是你會注意到它會變得有點混亂,在大多數情況下你可能更喜歡選項1。特別是當你有額外的好處時,明確命名的測試可以更容易地確定究竟出了什麼問題。

procedure MyTests.TestMultipleBadCasesInTheSameTest; 
begin 
    try 
    DoSomething('Bad1'); 
    //This time, although you're expecting an exception and lines 
    //here shouldn't be executed: 
    //**You've taken on the responsibility** of checking that an 
    //exception is raised. So **if** the next line is called, the 
    //expected exception **DID NOT HAPPEN**! 
    Fail('Expected exception for case 1 not raised'); 
    except 
    //Swallow the expected exception only! 
    on ESomethingBadHappened do; 
    //One of the few times doing nothing and simply swallowing an 
    //exception is the right thing to do. 
    //NOTE: Any other exception will escape the test and be reported 
    //as an error by DUnit 
    end; 

    try  
    DoSomething('Bad2'); 
    Fail('Expected exception for case 2 not raised'); 
    except 
    on E: ESomethingBadHappened do 
     CheckEquals('ExpectedErrorMessage', E.Message); 
     //One advantage of the manual checking is that you can check 
     //specific attributes of the exception object. 
     //You could also check objects used in the DoSomething method 
     //e.g. to ensure state is rolled back correctly as a result of 
     //the error. 
    end; 
end; 

NB! NB!在選項2中需要注意的一點非常重要。您需要注意您吞下的異常類。 DUnit的Fail()方法引發了一個ETestFailure異常,向框架報告測試失敗。而且您不希望意外吞下將導致預期異常的測試失敗的異常。

與異常測試相關的細微問題使得它非常重要:先測試,確保您有正確的失敗,然後才執行生產代碼更改才能通過測試。這個過程將顯着減少啞測試的可能性。

+1

Doh當然,在我調用一個錯誤的例程後,下面的行將永遠不會被執行.....並且我教導使用異常,最後是所有段時間。感謝您的解釋和正確的方法 - 兩個答案。我可以看到1和2都有自己的位置。 –

4

這兩種方法,StartExpectingExceptionStopExpectingException並不意味着可以直接調用。

相反,你應該使用ExpectedException屬性。當您設置此屬性時,將調用StartExpectingException。雖然你可以打電話StartExpectingException我相信,預期的用法是,你分配到ExpectedException

至於StopExpectingException,你不叫它。該框架稱之爲。它在TTestCase.RunTest這是執行你的測試方法的框架代碼。

所以,你的測試用例的代碼可能是這樣的:

ExpectedException := ESomeException; 
raise ESomeException.Create(...); 

當你的國家,你期待一個例外,你說的是,你的測試方法將引發該異常。由於引發異常會改變控制流,因此引發異常後出現的代碼將不會執行。異常傳播到調用堆棧直到被捕獲。該框架將在TTestCase.RunTest中發現異常。如果您表示預計發現異常,則測試將通過,否則將記錄失敗。

所有這些的最終結果是,如果測試方法的最終行爲是提高預期的異常,則可以使用ExpectedException機制。如果您想在引發異常之後執行進一步的測試,則ExpectedException機制根本沒有用處。如果你想這樣做,那麼你應該:

  1. 寫你自己的異常處理代碼,在你的測試方法,檢查異常是否按設計引發。
  2. 使用CheckException
+0

你的意思是「不打算直接打電話」在第一行。我看到了預期的用法。看起來很明智。爲了減少測試方法的數量,我在一個測試方法中進行了一系列測試,其中一些測試方法是邊界測試 - 這個或者沒有設置。根據你的描述,使用預期的異常並不是真的可以把這些放在測試方法中。我會研究CheckException –

+0

是的,「不是要直接調用」 –

相關問題