Quick Start Guide

vincentparrett edited this page Oct 4, 2011 · 11 revisions
Clone this wiki locally

##Delphi-Mocks

Delphi-Mocks is simple mocking framework for Delphi. It's intended to be used in conjunction with unit testing. The api is fluent where possible, and strongly typed where possible.

###RTTI on interfaces

Delph-Mocks supports mocking interfaces and classes, and is only supported in Delphi XE2 (actually mocking classes works with XE, but not interfaces).. An interface must have Runtime Type Information (RTTI), to enable RTTI on an interface, add the {$M+} compiler directive before it.

{$M+}
IFoo = interface
 function Bar(param : integer) : string;overload;
 function Bar(param : integer; param2 : string) : string;overload;
 procedure TestMe;
end;

###Creating the Mock.

We make extensive use of Generics in the framework. To create the mock, we use the TMock<T>.Create class function where T is the interface we want to mock.

procedure Test;
var
  mock : TMock<IFoo>; //our mock object
begin
  //Create our mock
  mock := TMock<IFoo>.Create;
 ...
end;

###Setting up the Mock behavior.

The behavior of the Mock is controlled by the TInterfaceMock<T>.Setup method. This method returns an ISetup<T> reference, where we can control what the return values of methods will be.

//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;

So lets setup the behavior of our IFoo Mock

//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;

###Setting our expectations of how the Mock will be used.

Setting expectations on a Mock allows you to verify that the interactions with your mock object were as you expected. Expectations are set through the Expect method of the Setup reference. Again we use a fluent style interface to define our expectations.

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;

Some checking is done when defining Expectations for overlapping or conflicting expectations, however the checking is very basic. Detected conflicting expectations will raise an exception.

###Using the Mock.

The TMock<T> type has an Instance function which returns a reference to our type T. So to call a method on our mock object, we can call it like this :

mock.Instance.Bar(1); 

TMock<T> also implements an Implicit operator overload that makes it castable to T

class operator Implicit(const Value: TMock<T>): T;

So if you have a procedure or method that takes a parameter of interface type T, you can just pass it your mock :

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;

###Verifying your Expectations

Verifying the expectations you defined for your Mock is as simple as calling :

mock.Verify("My Expectations were not met!!"); //The message is optional.

If all expectations were met, then the Verify method does nothing, however if any are not met, an EMockVerificationException is raised which will report the failures, e.g. :

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.