Открыл для себя Mock FrameWork. Работает, начиная с XE2. Основное назначение – эмуляция тестовых случаев в юнит тестах, “а что будет с методом, если переменная будет такая, а другая?”
Далее, я сделаю некоторый вольный перевод статьи с GitHub. Mock FrameWork поддерживает интерфейсы и классы. Интерфейс должен иметь RTTI информацию, на борту, для этого необходимо включить директиву {$M+}
{$M+}
IFoo = interface
function Bar(param : integer) : string;overload;
function Bar(param : integer; param2 : string) : string;overload;
procedure TestMe;
end;
Авторы фрэймворка широко используют Generics, например так
procedure Test;
var
mock : TMock<IFoo>; //our mock object
begin
//Create our mock
mock := TMock<IFoo>.Create;
...
end;
Настройка ожидаемого поведения задается следующим образом
//We use the Setup to configure our expected behaviour rules and to verify
//that those expectations were met.
ISetup<T> = interface
//Set Expectations for methods
function Expect : IExpect<T>;
//set the return value for a method when called with the parameters specified on the When
function WillReturn(const value : TValue) : IWhen<T>;
//Will exedute the func when called with the specified parameters
function WillExecute(const func : TExecuteFunc) : IWhen<T>;overload;
//will always execute the func no matter what parameters are specified.
procedure WillExecute(const AMethodName : string; const func : TExecuteFunc);overload;
//set the default return value for a method when it is called with parameter values we
//haven't specified
procedure WillReturnDefault(const AMethodName : string; const value : TValue);
//set the Exception class that will be raised when the method is called with the parmeters specified
function WillRaise(const exceptionClass : ExceptClass; const message : string = '') : IWhen<T>;overload;
//This method will always raise an exception.. this behavior will trump any other defined behaviors
procedure WillRaise(const AMethodName : string; const exceptionClass : ExceptClass; const message : string = '');overload;
end;
Настроим ожидаемое поведение в нашем примере
//setup a default return value for method Bar
mock.Setup.WillReturnDefault('Bar','hello world');
//setup explicit return values when parameters are matched
mock.Setup.WillReturn('blah blah').When.Bar(1);
mock.Setup.WillReturn('goodbye world').When.Bar(2,'sdfsd'); //call an overloaded method
//calling Bar with a value of 20 will invoke the supplied anonymous function
mock.Setup.WillExecute(
function (const args : TArray<TValue>; const ReturnType : TRttiType) : TValue
begin
//Note - args[0] is the Self interface reference for the anon method, our first arg is [1]
result := 'The result is ' + IntToStr(args[1].AsOrdinal);
end
).When.Bar(200);
mock.Setup.WillRaise(EDontCallMeIllCallYou,'You called me when I told you not to!').When.TestMe;
Настройка наших ожиданий
mock.Setup.Expect.AtLeastOnce.When.Bar(1);
mock.Setup.Expect.AtLeastOnce.When.Bar(99);
mock.Setup.Expect.Between(2,4).When.Bar(23);
mock.Setup.Expect.Exactly('Bar',5);
mock.Setup.Expect.Never.When.TestMe;
Если результат не совпадет с нашими ожиданиями, выпадет Exception
У TMock<T> есть ссылка на объект, то есть мы можем сделать так
mock.Instance.Bar(1);
TMock<T> также поддерживает оператор Implicit, который позволяет его приводить к типу T
class operator Implicit(const Value: TMock<T>): T;
Далее приводится такой пример теста
procedure Test;
var
mock : TMock<IFoo>; //our mock object
procedure TestImplicit(value : IFoo);
begin
WriteLn('Calling Bar(1234567) : ' + value.Bar(1234567));
end;
begin
//Create our mock
mock := TMock<IFoo>.Create;
TestImplicit(mock); //uses the Implicit Operator overload
end;
Проверка наших ожиданий
mock.Verify("My Expectations were not met!!"); //The message is optional.
Если все ожидания совпали, фрэймворк не заругается, иначе получим такую ошибку
EMockVerificationException: My Expectations were not met!!
Method : Bar
Expectation [ At Least Once When( 99 ) ] was not met.
Expectation [ Between 2 and 4 Times When( 23 ) ] was not met.
Method : TestMe
Expectation [ Never When( ) ] was not met.