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
) к первому кроме или наконец блок найден в колл-стек.
Writeln('End');
не будет называться.
- Строка
//3
не будет называться.
- Какой бы код не существовал в кроме блок
OuterStep
выполнен следующим.
- И, наконец, когда
raise;
называется, исключение ререйз и указатель команд переходит к следующему кроме или наконец блока.
- Обратите внимание, что как рейз; любое другое исключение в пределах , за исключением, также выскочит блок (фактически скрывая первое исключение).
Так что, когда вы пишете:
StartExpectingException(...);
DoSomething();
StopExpectingException(...);
Есть 2 варианта:
DoSomething
вызывает исключение и StopExpectingException
никогда не называли.
DoSomething
не вызывает исключения, и когда StopExpectingException
называется, не исключение.
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
Как предположил Дэвид, вы можете написать свой собственный обработки внутри теста исключение. Но вы заметите, что это может стать немного грязным, и вы, вероятно, предпочтете вариант 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. Вам нужно быть осторожным в том, какой класс исключений вы проглотите. Метод Fail()
DUnit вызывает исключение ETestFailure
, чтобы сообщить об этом в раму, что тест не прошел. И вы не захотите случайно проглотить исключение, которое вызовет ошибку проверки для ожидаемого исключения.
Тонкие проблемы, связанные с проверкой исключений, делают важным: сначала проверить, убедиться, что у вас есть правильный сбой, и только затем выполнить смену производственного кода, чтобы получить пропуск. Этот процесс значительно снизит шансы теста на dud.
Допустим, следующая строка после моего вызова ошибочной процедуры никогда не будет выполнена ..... и я преподаю использование исключений и, наконец, разделы все время. Спасибо за объяснение и правильный способ сделать это - оба ответа. Я вижу, что 1 и 2 оба имеют свое место. –