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

时间:2016-03-20 12:06:14

标签: delphi exception delphi-7 dunit

以下代码工作正常,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);

2 个答案:

答案 0 :(得分:4)

这两种方法StartExpectingExceptionStopExpectingException并不是直接调用的。

相反,您应该使用ExpectedException属性。设置此属性时,将调用StartExpectingException。虽然您可以致电StartExpectingException,但我相信您预期的用途是指定给ExpectedException

至于StopExpectingException,你不会打电话给它。框架称之为。它在TTestCase.RunTest中执行,这是执行测试方法的框架代码。

因此,您的测试用例代码可能如下所示:

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

当你声明你期待一个异常时,你所说的是你的测试方法会引发异常。由于引发异常会改变控制流,因此引发异常后出现的代码将不会执行。异常会在调用堆栈中向上传播,直到它们被捕获为止。该框架将捕获TTestCase.RunTest中的异常。如果您已指出捕获的异常是预期的,则测试将通过,否则将记录失败。

所有这一切的最终结果是,如果测试方法的最终行为是提出预期的异常,则可以使用ExpectedException机制。如果要在引发异常后执行进一步的测试,则ExpectedException机制根本没用。如果你想这样做,你应该:

  1. 在测试方法中编写自己的异常处理代码,以检查是否按设计引发异常。
  2. 使用CheckException

答案 1 :(得分: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)到第一个 finally 块之外在call-stack中。
  • Writeln('End');将不会被调用。
  • 不会调用行//3
  • 下一步执行OuterStep块之外的任何代码。
  • 最后,当调用 raise; 时,会重新引发异常并且指令指针跳转到下一个最终阻止。
  • 另请注意,提升; 块内的任何其他例外也会跳出来,(有效隐藏第一个例外)。

所以当你写:

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

有两种可能性:

  1. DoSomething引发异常,永远不会调用StopExpectingException
  2. DoSomething不会引发异常,并且在调用StopExpectingException 没有例外。
  3. 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

    正如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!注意! 在选项2中需要注意的一点非常重要。您需要注意您吞下的异常类。 DUnit的Fail()方法引发ETestFailure异常以向框架报告测试失败。并且您不希望意外地吞下将导致预期异常的测试失败的异常。

    与细微问题相关的异常测试使得重要的是:首先测试,确保您有正确的失败,然后才实施生产代码更改以获得通过。这个过程将大大降低哑弹测试的可能性。