4

以下のコードは正常に動作し、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);
4

2 に答える 2

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、 line//2は内部で例外を発生させますInnerStep。例外が発生するたびに:

  • 命令ポインターは、各メソッドから( に少し似ています)ジャンプしてgoto、呼び出しスタックで見つかった最初のexceptまたはfinallyブロックに移動します。
  • Writeln('End');呼び出されません。
  • ライン//3は呼び出されません。
  • のexceptブロックに存在するコードがOuterStep次に実行されます。
  • 最後にraise;が呼び出されると、例外が再発生し、命令ポインタが次のexceptまたはfinallyブロックにジャンプします。
  • 同様にレイズすることにも注意してください。例外ブロック内の他の例外も飛び出します (最初の例外を効果的に隠します)。

だからあなたが書くとき:

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

2 つの可能性があります。

  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;

注意!注意!オプション 2 で注意すべき非常に重要なことがあります。飲み込む例外クラスに注意する必要があります。DUnit のFail()メソッドはETestFailure例外を発生させ、テストが失敗したことをフレームワークに報告します。また、予期される例外のテストの失敗をトリガーする例外を誤って飲み込みたくはありません。

例外テストに関連する微妙な問題により、最初にテストし、正しい失敗があることを確認してから、合格を得るために製品コードの変更を実装することが重要になります。このプロセスにより、不発試験の可能性が大幅に減少します。

于 2016-11-05T04:07:43.330 に答える