Saturday, 2 June 2012

Mocking methods

I have been using Mock objects a lot with Delphi and I have implemented a small example and that you can find available in my personal repository on Google code. This simple example plays with the signature of the methods and uses delegates to return what we want to return. There are lots of ways to mock objects and lots of really good available frameworks: PascalMock, Ultra Basic MockObjects, DelphiMocks, etc. but sometimes we just want to mock some of the methods reintroducing a new signature. This simple example has helped me a lot in my unit testing and I hope you find it enough interesting to use it.


Basically we need an Interface to Mock which will contain all the contracts of the instance. TMock will be mocking the Interface and we need to set up the methods on the mockable class in a particular way using delegates. With this method TMock will be able to inject a new delegate and use it in our unit testing.

The declaration of TMock is quite neat:

type
  IMock = interface
  end;

  TMock<T> = class(TInterfacedObject,IMock)
  private
    FMockClass : T;
  public
    function SetUp() : T;
    constructor Create(mockingClass : T);
  end;

{ TMock<T> }

constructor TMock<T>.Create(mockingClass: T);
begin
  FMockClass := mockingClass;
end;

function TMock<T>.SetUp(): T;
begin
  Result :=  FMockClass;
end;
Then the IServer interface will have two methods which I want to mock. The delegates on the IServer will have to be extended as a property to then be able to overwrite them during the unit testing.

  //Contract definition to be Mocked.
  IServer<T, TResult> = Interface
    //Send structure
    function GetSendMessage() : TFuncParam<T, TResult>;
    procedure SetSendMessage(const Value: TFuncParam<T, TResult>);
    property SendMessage : TFuncParam<T, TResult> read GetSendMessage write SetSendMessage;
    //Receive structure
    function GetReceiveMessage() : TFunc<T>;
    procedure SetReceiveMessage(const Value: TFunc<T>);
    property ReceiveMessage : TFunc<T> read GetReceiveMessage write SetReceiveMessage;
  End;

It is really important to define the visibility of the methods on the implementation side so the mock only sees the methods we really want to redefine.

Then when the mock test is defined, it will only display the methods available to inject:
To set up the test, we only need to use the following declaration:

procedure TestTProtocolServer.TestCommunicateWithMock;
var
  ReturnValue: Boolean;
  Server: IServer<String, Boolean>;
  mock : TMock<IServer<String, Boolean>>;
begin
  Server := TServer.Create;

  mock := TMock<IServer<String, Boolean>>.Create(Server);

  //Set up the mocking methods using delegates:
  mock.SetUp().SendMessage := (function (message : string) : boolean
  begin
    result := True; //Return always true whatever the message is
  end);

  mock.SetUp().ReceiveMessage := (function () : string
  begin
    //Return the same message the server would reply
    result := 'This is the message from the server!';
  end);

  FProtocolServer := TProtocolServer.Create(mock.SetUp());
  try
    ReturnValue := FProtocolServer.Communicate;
    CheckTrue(ReturnValue, 'Communication with the Server Failed');
  finally
    Server := nil;
    mock.Free;
    FProtocolServer.Free;
    FProtocolServer := nil;
  end;
end;

With this simple method we can reuse existing code and just rewrite what we need to mock as the method we are calling depends on other objects/libraries, etc which are not testable. Note that this is not a framework, it is just a simple example where I need to mock a method and I don't want to use any of the existing frameworks. I like the way moq works and I wanted something similar (I wish we had lambda expressions, maybe one day!):

var mock = new Mock<ITest>();

mock.Setup(p => p.SendMessage(Is.Any<String>))
    .Returns(true)

It has loads of drawbacks as you will need to change the signature of your methods using delegates and sometimes it is not easy to do it, but I have been using it and it does the job.

Please, have a look at the repository and do not hesitate to leave any comment.

7 comments:

  1. No offense, but: Wow, that was a nice example on how to totally overengineer something that we already could do with good ol' Delphi 7 or earlier.

    A few things:
    I totally fail to understand the need of that TMock thing, it does nothing than storing the interface reference you have anyway. Why not call your Server variable mock.

    If you have to change your code to make it unit testable, something is wrong. That can either mean you don't have unit testable code at all due to some design principle violation. Then changing your code is good since you end up with easier to maintain and test code.

    If you need to change your code because your testing framework or the way you are testing requires it - that is bad!

    The IServer interface seriously is beyond anything you would use in production (except it really is a pluggable system where you can exchange the callbacks, but this is solely for your way of using a mock). Making an interface open for something that is not meant in production code - is bad!

    I added an example to my svn repository to show how I would to this with less bloat code:http://code.google.com/p/delphisorcery/source/browse/#svn%2Ftrunk%2FSamples%2FMocks

    ReplyDelete
    Replies
    1. Hi Stefan,

      No offence taken. That's why I keep publishing stuff on my blog. It is always nice to get different opinions on different ideas I have and it is always important to be independent of the good opinion of others (otherwise I would be afraid of publishing all the time).

      I have seen your example and it does the job, but my point here is that I would like to find a way to do it like moq does which is great.

      I had to implement a similar approach when dealing with RPC servers and my IServer was expecting an XML response from the server which wasn't there when testing (the method would throw and exception like 'server is not connected' or something like that). That's why I had to implement a way to mock the method of the server. Of course I'm re-engineering the whole system and I'm sure there are simpler ways to do that, but I wanted to keep the notation, where I inject my object and I redefine one of it's methods.

      In my humble opinion this code gives you new ideas to start off and it helps you to understand few principles (by breaking others).

      Thanks again for your comments as I'm always looking forward to them. Your DSharp project is amazing and I always have a look at it when looking for inspiration.
      Jordi

      Delete
    2. Thanks, I am always trying to bring Delphi to it's limits and keep up with new or old ideas from other languages (sometimes a frustrating task when taking a glimpse at C# for example I have to admit) ;)

      You have your system under test with a requirement (IServer) which you need to mock. Now you specify the behaviour of that mock (like returning True in all cases or something).

      What you were doing is creating the dependency anyway and implementing something into it that makes it "mockable".

      This is not something you should do because it might be the case that your TServer that you want to mock has another dependency and so on. Your seam in this case is IServer. Nothing beyond that should be required in your unit test.

      As far as I have seen, Moq does not require an implementation of the interface it is mocking which you did. And exactly that is what DelphiMocks and DSharp are doing.

      I can create an example with DSharp later if you like.

      Delete
    3. Thanks Stefan, that would be brilliant!.

      I know how it feels when developing with C# as it amazing the stuff we can do with it and how behind is Delphi in that matter.

      I can see your point now with my example and you are right. I will amend it later on based on your example which I will be looking forward to it.

      Thanks again, your input is always very interesting.

      Jordi

      Delete
    4. Just commited.

      While we can't do fancy stuff like lambda expressions when specifying the parameters the features of DSharp (which is very similar to DelphiMocks feature wise afaik just a slightly different syntax) they are very powerful: you can define expectations and optionally check if they all were met. What is not there (yet) is defining the exact order of the expectations like you can do in some mocking frameworks.

      Delete
    5. Thanks Stefan, that's a very beautiful example, spot on!.

      Delete
  2. Hi Jordi,

    I am working with a brand to build an engaging content community that covers topics relating to tech news, software, API and mobile apps development.

    We’ve taken a read through your blog and we think you’ve done a fantastic job covering topics that our brand's audience would also be interested in covering. It would be great if you could join our community to help educate, inform and converse with those in the tech industry.

    If you would like to learn more about this, please send an email to info@atomicreach.com

    Thanks,
    Annette

    ReplyDelete