以下代码工作正常,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);
答案 0 :(得分:4)
这两种方法StartExpectingException
和StopExpectingException
并不是直接调用的。
相反,您应该使用ExpectedException
属性。设置此属性时,将调用StartExpectingException
。虽然您可以致电StartExpectingException
,但我相信您预期的用途是指定给ExpectedException
。
至于StopExpectingException
,你不会打电话给它。框架称之为。它在TTestCase.RunTest
中执行,这是执行测试方法的框架代码。
因此,您的测试用例代码可能如下所示:
ExpectedException := ESomeException;
raise ESomeException.Create(...);
当你声明你期待一个异常时,你所说的是你的测试方法会引发异常。由于引发异常会改变控制流,因此引发异常后出现的代码将不会执行。异常会在调用堆栈中向上传播,直到它们被捕获为止。该框架将捕获TTestCase.RunTest
中的异常。如果您已指出捕获的异常是预期的,则测试将通过,否则将记录失败。
所有这一切的最终结果是,如果测试方法的最终行为是提出预期的异常,则可以使用ExpectedException
机制。如果要在引发异常后执行进一步的测试,则ExpectedException
机制根本没用。如果你想这样做,你应该:
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(...);
有两种可能性:
DoSomething
引发异常,永远不会调用StopExpectingException
。DoSomething
不会引发异常,并且在调用StopExpectingException
时没有例外。 David has explained DUnit框架为您调用StopExpectingException
。但您可能想知道如何处理检查多个异常情况的测试用例。
写下较小的测试
你知道每个人都说你应该做的事情在任何情况下都是正确的吗? :)
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;
正如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
异常以向框架报告测试失败。并且您不希望意外地吞下将导致预期异常的测试失败的异常。
与细微问题相关的异常测试使得重要的是:首先测试,确保您有正确的失败,然后才实施生产代码更改以获得通过。这个过程将大大降低哑弹测试的可能性。