2016-03-20 7 views
4

. Нижеприведенный код работает нормально, calc ... генерирует исключение, комментирует его или меняет calc ..., чтобы не выбрасывать и исключать, а тест терпит неудачу.Проверка Delphi 7 Dunit после StopExpectingException не работает, как я ожидаю.

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'); 

терпит неудачу на 1-й 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

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 варианта:

  1. DoSomething вызывает исключение и StopExpectingException никогда не называли.
  2. 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

Допустим, следующая строка после моего вызова ошибочной процедуры никогда не будет выполнена ..... и я преподаю использование исключений и, наконец, разделы все время. Спасибо за объяснение и правильный способ сделать это - оба ответа. Я вижу, что 1 и 2 оба имеют свое место. –

4

Эти два метода, StartExpectingExceptionStopExpectingException и не предназначены, чтобы быть вызван непосредственно.

Вместо этого вы должны использовать свойство ExpectedException. Когда вы устанавливаете это свойство, вызывается StartExpectingException. Хотя вы могли позвонить по номеру StartExpectingException, я верю, что предполагаемое использование - это то, что вы назначаете ExpectedException.

Что касается StopExpectingException, вы не называете это. Рамка называет это. Он делает это в TTestCase.RunTest, код рамки, который выполняет ваш тестовый метод.

Так что ваш код случае тест может выглядеть следующим образом:

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

Когда вы заявляете, что вы ожидаете, исключение, о чем вы говорите, что ваш метод испытания поднимет это исключение. Так как повышение исключения изменяет поток управления, код, который появляется после исключения, не будет выполняться. Исключения распространяют стек вызовов до тех пор, пока они не будут пойманы. Рамка поймает исключение в TTestCase.RunTest. Если вы указали, что ожидаемое исключение ожидается, тогда тест пройдет, в противном случае произойдет сбой.

Чистый результат всего этого заключается в том, что механизм ExpectedException может использоваться, если последний акт метода испытаний заключается в том, чтобы повысить ожидаемое исключение. Механизм ExpectedException не используется вообще, если вы хотите выполнить дополнительные тесты после возникновения исключения. Если вы хотите это сделать, вам необходимо либо:

  1. Напишите свой собственный код обработки исключений в вашем методе тестирования, который проверяет, что исключения подняты в соответствии с запросами.
  2. Использование CheckException.
+0

Вы имеете в виду, что «не должны быть вызваны непосредственно» в первой строке. Я вижу ожидаемое использование. Кажется очень разумным. чтобы уменьшить количество методов тестирования, я поставил кучу тестов в одном методе тестирования, а некоторые из них были пограничными тестами - то или иное не задано. Основываясь на вашем описании, на самом деле это невозможно поместить в тестовый метод с использованием ожидаемого исключения. Я рассмотрю CheckException –

+0

Да, «не должны быть вызваны напрямую» –