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
。现在每当引发异常时:
- 指令指针从每个方法(有点像)跳到调用堆栈中找到的第一个except或finally块。
goto
Writeln('End');
不会被调用。
//3
不会调用线路。
- 接下来执行except块中存在的任何代码。
OuterStep
- finally
raise;
被调用时,异常被重新引发,指令指针跳转到下一个except或finally块。
- 还要注意像raise; except块中的任何其他异常也会跳出,(有效地隐藏第一个异常)。
所以当你写:
StartExpectingException(...);
DoSomething();
StopExpectingException(...);
有2种可能:
DoSomething
引发异常并且StopExpectingException
永远不会被调用。
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
异常以向框架报告测试失败。而且您不希望意外吞下会触发预期异常测试失败的异常。
与异常测试相关的细微问题很重要:首先测试,确保您有正确的失败,然后才实施生产代码更改以获得通过。该过程将显着减少哑测试的机会。