簡體   English   中英

StopExpectingException之后的Delphi 7 Dunit檢查不能正常工作

[英]Delphi 7 Dunit checks after StopExpectingException are not working as I expect

下面的代碼工作正常,calc ...生成異常,將其注釋掉或更改calc ...以不拋出異常並且測試失敗。

  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);

這兩個方法StartExpectingExceptionStopExpectingException並不是直接調用的。

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

至於StopExpectingException ,你不要調用它。 框架稱之為。 它在TTestCase.RunTest中執行,這是執行測試方法的框架代碼。

所以你的測試用例代碼可能如下所示:

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

當你聲明你期待一個異常時,你所說的是你的測試方法會引發異常。 由於引發異常會改變控制流,因此引發異常后出現的代碼將不會執行。 異常會在調用堆棧中向上傳播,直到它們被捕獲為止。 該框架將捕獲TTestCase.RunTest的異常。 如果您已指示捕獲的異常是預期的,則測試將通過,否則將記錄失敗。

所有這一切的最終結果是,如果測試方法的最終行為是提出預期的異常,則可以使用ExpectedException機制。 如果要在引發異常后執行進一步的測試,則根本不使用ExpectedException機制。 如果你想這樣做,你應該:

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

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上面調用OuterStep ,第//2行將在InnerStep內引發異常。 現在每當出現異常時:

  • 指令指針跳出每個方法 (有點像goto )到調用堆棧中找到的第一個除外最后一個塊。
  • Writeln('End'); 不會被叫。
  • 不會調用第//3行。
  • 接下來執行OuterStepexcept塊中存在的代碼。
  • 最后raise; 調用異常,異常重新引發,指令指針跳轉到下一個除了最后一個塊。
  • 還要注意像加注; except塊中的任何其他異常也會跳出來(有效地隱藏第一個異常)。

所以當你寫:

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

有兩種可能性:

  1. DoSomething引發異常,從不調用StopExpectingException
  2. DoSomething不引發異常,當StopExpectingException 被稱為是沒有例外。

David解釋說DUnit框架為您調用StopExpectingException 但您可能想知道如何處理檢查多個異常情況的測試用例。

選項1

寫小測試。
你知道每個人都說你應該做的事情在任何情況下都是正確的嗎? :)
例如

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

正如David建議的那樣,您可以在測試中編寫自己的異常處理。 但是你會注意到它會變得有點混亂,在大多數情況下你可能更喜歡選項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異常,向框架報告測試失敗。 並且您不希望意外地吞下將導致預期異常的測試失敗的異常。

與細微問題相關的異常測試使得重要的是:首先測試,確保您有正確的失敗,然后才實現生產代碼更改以獲得通過。 該過程將顯着降低啞彈測試的可能性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM