CheckException принимает только методы с 0 параметрами; как проверить, что другие методы вызывают исключения?

Мне интересно, как лучше всего проверять исключения в dunit. Я не очень хорошо знаком с указателями методов в Delphi. Есть ли возможность привязать аргументы к указателю метода, чтобы его можно было вызывать без аргументов. На данный момент я всегда пишу дополнительный метод, который делает эту «привязку» вручную. Это будет раздражать, если у SUT много методов броска.

// What i did before i knew abput CheckExcepion
procedure MyTest.MyMethod_BadInput_Throws;
var
    res: Boolean;
begin
    res := false;
    try
        sut.MyMethod('this is bad');
    except
        on e : MyExpectedException do:
            res := true;
    end;
    CheckTrue(res);
end;

// What i do now
procedure MyTest.MyMethodWithBadInput;
begin
    sut.MyMethod('this is bad');
end;

procedure MyTest.MyMethod_BadInput_Throws;
begin
    CheckException(MyMethodWithBadInput, MyExpectedException);
end;

// this would be nice
procedure MyTest.MyMethod_BadInput_Throws;
begin
    CheckException(
        BindArguments(sut.MyMethod, 'this is bad'),  // <-- how to do this
        MyExpectedException);
end;

person hansmaad    schedule 06.01.2011    source источник


Ответы (5)


Вы можете использовать StartExpectingException, чтобы окружить вызов вашего метода).

StartExpectingException(MyException);
MyMethod(MyParam);
StopExpectingException();
person TridenT    schedule 06.01.2011
comment
+1 это выглядит как правильный способ сделать это; спасибо, что научили меня чему-то новому! - person David Heffernan; 06.01.2011
comment
Или, что еще лучше, просто установите свойство ExpectedException внутри вашего теста. - person Erick Sasse; 06.01.2011
comment
Может быть, добавить попробовать... наконец здесь? - person Jeroen Wiert Pluimers; 06.01.2011
comment
@Jeroen: Нет, не добавляйте попытку... наконец. На самом деле это приведет к неправильному сбою теста, даже если возникнет исключение. StopExpectingException реализовано как: Меня не должны звать; если это так, то исключение не возникло, поэтому тест провален. Помещение его в finally просто заставляет его провалить тест. Строго говоря, StopExpectinException вообще не нужен. Если метод тестирования завершается нормально в состоянии ExpectedException, тест завершается неудачно. - person Disillusioned; 01.04.2011
comment
Увидите, что MyMethod вызовет исключение, и после него ничего не будет выполнено. Если у вас есть более одной проверки, они не будут выполнены. - person neves; 27.10.2011

Я не знаю, поддерживает ли его DUnit, но это идеальный вариант использования анонимных методов, которые были введены в Delphi 2010. Если DUnit не поддерживает его, вы можете легко изменить исходный код самостоятельно.

person David Heffernan    schedule 06.01.2011
comment
Звучит хорошо, но как определить анонимный метод? CheckException берет TTestMethod = procedure of object. Моя первая попытка CheckException(procedure begin raise Exception.Create('Error'); end, Exception); Не компилируется (несовместимые типы-> обычный proc, метод ptr) - person hansmaad; 06.01.2011
comment
@hansmaad Вам нужно определить анонимную процедуру как переменную в вашей тестовой функции. И вам нужно будет добавить CheckException в TTestCase, который имеет reference to procedure в качестве параметра, а не procedure of object. Однако ответ @TridenT на использование StartExpectingException кажется правильным способом сделать это. - person David Heffernan; 06.01.2011
comment
Это именно то, что я правильно знал, и это работает. Нужно перегрузить метод CheckException, взяв TTestProc вместо TTestMethod. Тогда вы можете использовать анонимные методы. Но StartExpectingException кажется подходящим способом, даже если мне не нравится, что я должен вызывать метод Stop... - person hansmaad; 06.01.2011
comment
@hansmaad Имейте в виду, что код после StopExpectingException не будет выполняться (по крайней мере, в Delphi 2006). - person Alois Heimer; 21.04.2014

Как уже отмечалось, это отличное место для анонимных методов.

Вот как я это делаю. Я «позаимствовал» это у Алекса Чобану:

procedure TestTMyClass.CheckException(aExceptionType: TClassOfException; aCode: TTestCode; const aMessage: String);
var
  WasException: Boolean;
begin
  WasException := False;
  try
    aCode;
  except
    on E: Exception do
    begin
      if E is aExceptionType then
      begin
        WasException := True;
      end;
    end;
  end;
  Check(WasException, aMessage);
end;

Затем вызовите его примерно так:

CheckException(ETestingException, 
             procedure begin FMyClass.RaiseTestingException end,      
             'The ETestingException exception didn''t get raised.  That is impossible!');
person Nick Hodges    schedule 10.04.2011
comment
Я сделал рабочую версию этого метода и разместил ее ниже. Большое спасибо за публикацию этого, это очень полезно для меня. - person DBedrenko; 17.03.2016

Использовать StartExpectingException() не лучший способ, если вы хотите протестировать более одного случая исключения. Чтобы проверить все возможные случаи в моей тестовой процедуре вместе с исключениями, я использую этот алгоритм:

uses
  Dialogs;
procedure MyTest.MyMethod_Test;
begin
  // Test for Exceptions
  try
    MyMethod(MyParam1CreatingException1);
    ShowMessage('Error! There should have been exception: Exxx here!');
    Check(false);
  except on E: Exception do Check(E is  ExceptionType1); end; // This exception is OK
  try
    MyMethod(MyParam2CreatingException2);
    ShowMessage('Error! There should have been exception: Exxx here!');
    Check(false);
  except on E: Exception do Check(E is  ExceptionType2); end; // This exception is OK
  // ... test other exceptions ...

  // Test other parameters
  CheckEquals('result1', MyMethod(MyParam1));
  CheckEquals('result2', MyMethod(MyParam2));
  // ... other tests ...
end;

Причина, по которой я использую ShowMessage('Error! There should be exception: Exxx here!'); вместо предоставленного метода Check(false, 'There should have been an EListError.');, заключается в том, что в моем случае (Delphi6) Check(boolean, 'Message') не работает - он не показывает сообщение, если проверка находится внутри блока try...except (не знаю почему).

person nfc1    schedule 05.08.2013

Это рабочая и улучшенная версия ответа Ника Ходжеса, который подклассифицирует DUnit TestFramework.TTestCase:

uses
  TestFramework, System.SysUtils;
type
  TTestCode = reference to procedure;

  TTestCasePlus = class(TestFramework.TTestCase)
    procedure CheckException(
      ExceptionType: TClass; Code: TTestCode; const Message: String = '');
  end;

implementation

procedure TTestCasePlus.CheckException(
  ExceptionType: TClass; Code: TTestCode; const Message: String = '');
{ Check whether some code raises a specific exception type.

Adapted from http://stackoverflow.com/a/5615560/797744

Example:

  Self.CheckException(EConvertError,
                      procedure begin UnformatTimestamp('invalidstr') end);

@param ExceptionType: The exception class which we check if it was raised.
@param Code: Code in the form of an anonymous method that should raise the
  exception.
@param Message: Output message on check failure. }
var
  WasRaised: Boolean;
begin
  WasRaised := False;
  try
    Code;
  except
    on E: Exception do
      if E is ExceptionType then
        WasRaised := True;
  end;
  Check(WasRaised, Message);
end;

Приятным бонусом к этому методу проверки того, было ли возбуждено исключение поверх Start/StopExpectingException(), является то, что вы можете запустить testrunner в отладочной сборке, и он не будет постоянно беспокоить вас «Исключение было поднято. Прервать? Продолжить?» каждый раз, когда возникает исключение, даже если оно было обработано.

person DBedrenko    schedule 17.03.2016