#1 2012-10-15 11:52:04

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Stubs and Mocks in mORMot

Our mORMot framework is therefore able to stub or mock any Delphi interface.

As usual, the best way to explain what a library does is to look at the code using it. Here is an example (similar to the one shipped with RhinoMocks) of verifying that when we execute the "forgot my password" scenario, we remembered to call the Save() method properly:

procedure TMyTest.ForgotMyPassword;
var SmsSender: ISmsSender;
    UserRepository: IUserRepository;
begin
  TInterfaceStub.Create(TypeInfo(ISmsSender),SmsSender).
    Returns('Send',[true]);
  TInterfaceMock.Create(TypeInfo(IUserRepository),UserRepository,self).
    ExpectsCount('Save',qoEqualTo,1);
  with TLoginController.Create(UserRepository,SmsSender) do
  try
    ForgotMyPassword('toto');
  finally
    Free;
  end;
end;

And... that's all, since the verification will take place when IUserRepository instance will be release.

Forum topic about related blog articles:
- http://blog.synopse.info/post/2012/10/1 … ith-mORMot
- http://blog.synopse.info/post/2012/10/1 … -and-mocks
- http://blog.synopse.info/post/2012/10/1 … -and-stubs

Offline

#2 2012-10-17 09:38:39

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Stubs and Mocks in mORMot

I love this one wink - great job.

Offline

#3 2012-10-18 07:01:50

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Stubs and Mocks in mORMot

I'm absolutely new for mocking conception, so I google a little bit and found good article what explain what "mocking" is. It may be useful for others (article about C++, but reading give good understanding of conception):
English version: http://code.google.com/p/googlemock/wiki/ForDummies
Russian version: http://code.google.com/p/googletest-tra … iesRussian

IMHO: Idea is good, but "the devil is in the details". To use mocking I must use interfaces. When I use interfaces I lost control on code, because I don't see implementation. Debugging an optimization became very hard. Especially if a beginner developer read something like GOF (Gang Of Four)  and  wherever necessary and where not use design templates like Visitor, Decorator and so on, and in debugging I don't understand at all what class actually implement passed interface. As for me, this is a biggest problem for .NET framework - developers use interfaces, don't look on implementation (and often don't have it in sources at all), do not learn by reading someone else's code and therefore produce monkey-code. This is only IMHO...

Last edited by mpv (2012-10-18 07:02:22)

Offline

#4 2012-10-18 07:22:06

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

Using interfaces is a bit difficult if you want to browse the code, as you say.
A similar issue exists when you use virtual methods: the exact method which will be run is defined at runtime, not at coding time: Ctrl+Click on a virtual method will lead you into the same level class implementation, even if it will be overriden in the actual class,
Therefore IMHO interfaces can be seen as "classes with only-virtual-methods".

If your interfaces are defined following SOLID principles, and if you take a look at the implementation, just as usual, it is easy to work with.
And mocking/stubbing helps testing your code, especially in test-driven mode, with a clear domain-driven design for instance, where each layer has a clear responsibility.
If your interfaces are decoupled and small, with clear focus, it is no problem for them to be virtual. And this is in your testing that you will focus on the implementation.

Your remark about .NET kind of coding does make sense to me.
It is exactly what I see every day.
You can do wonders in .NET (and in Java), reaching amazing speed and efficiency.
But if you use the framework and write code using the "black box" approach, forgetting about the implementation (I think 80% of .Net coders do, even worse for "young" coders), the code logic is there, but the code efficiency is not. And it is even worse is you use LINQ for objects, and such patterns: you get the information you need, but may retrieve the whole database content just to get one small data.
Huge performance problems and scalability issues come directly from such blindness.

With mORMot, we tried to give at hand all means of producing fluent and efficient code:
- Framework core was designed with performance in mind;
- High-level patterns are included (CRUD, Batch, caching, services, routing, security, serialization), and optimized, ready to be re-used;
- You have some very efficient engines at hand (like TSQLRestServerStaticInMemory), if performance is needed;
- Every framework aspect is decoupled: you can use some part of it (this is exactly your case, AFAIR);
- Framework makes layers and hosting easy to implement and efficient to work with: you will never be afraid of decoupling your application logic, and with interfaces, it will be dead easy to maintain and make evolve your application;
- Logging is integrated, including customer-side profiling: you will see instantly if a DB request is over-called, and find out real bottlenecks.
Goal is to focus on business logic.

I'm currently working on huge C# service design, in my day work.
Every day, I found out how mORMot KISS architecture is perhaps less powerful than WCF/WAS/ISS, but also how verbose and sometimes difficult it is to work with. Just try to write a remote service: you will have to write a proxy access code on both sides, with one endpoint per interface. A nightmare! Just compare with mORMot ServiceRegister() methods on both client and server side, and you'll be convinced.

Offline

#5 2012-10-19 08:35:39

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Stubs and Mocks in mORMot

AB, the blog title for http://blog.synopse.info/post/2012/10/1 … e-not-evil must be: "Interfaces are not evil; or are Delphi-ers the new Vampires?; or why MPV must study english" smile
I don't want trolling, the more to be rude - it's just because of MY translation problems! The idiom "the devil is in the details" means just  -  "details are important". Interfaces is good sometimes, for example it very good for "between processes" communication between Delphy and C++ code (like in OleDB in mORMot), but my opinion - for "inner processes" use it make code less understood.

Last edited by mpv (2012-10-19 11:51:56)

Offline

#6 2012-10-19 11:48:49

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

@mpv
This was exactly my point.

Offline

#7 2012-10-19 11:54:18

mpv
Member
From: Ukraine
Registered: 2012-03-24
Posts: 1,534
Website

Re: Stubs and Mocks in mORMot

@AB - I edit previous post - seems I wrote again not what I had in mind.

Offline

#8 2012-11-23 10:07:36

Stefan
Member
Registered: 2012-11-23
Posts: 26

Re: Stubs and Mocks in mORMot

The biggest problem with that implementation is that any typo or refactoring will break your code (isn't that one of the biggest complaints about LiveBindings also?)

Offline

#9 2012-11-23 10:13:55

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

@Stefan
Yes, we use method names as text.
Using some features like generics may help in some case, but it is not a 100% strong typing...

But by design, even if code may break at runtime, it will be during stub/mock definition (with an error about an unknown method name), so it won't be an issue for testing. Test will fail. But no false negative would occur. I suspect this is not an issue.

How refactoring will break the code in your opinion?
You do not need to specify all parameters every time, and you can add methods to any interface without breaking the stubbing/mocking code.

Offline

#10 2012-11-23 12:29:32

Stefan
Member
Registered: 2012-11-23
Posts: 26

Re: Stubs and Mocks in mORMot

ab wrote:

How refactoring will break the code in your opinion?
You do not need to specify all parameters every time, and you can add methods to any interface without breaking the stubbing/mocking code.

Renaming a method or changing signature will cause the test to fail at runtime, not at compiletime. And that is for every test that uses a mock of the changed interface.

Using strings as identifier is one of the major weak points of certain mock frameworks, while others solve that problem by avoiding that.

I think even without generics (although they allow a much nicer fluent interface style to define expectations) this is possible (not perfect though)

procedure TMyTest.ForgotMyPassword;
var
  SmsSender: ISmsSender;
  UserRepository: IUserRepository;
begin
  TInterfaceStub.Create(TypeInfo(ISmsSender), SmsSender);
  When(SmsSender.Send()).Returns(True);
  TInterfaceMock.Create(TypeInfo(IUserRepository), UserRepository, Self);
  When(UserRepository.Save()).ExpectsCount(qoEqualTo, 1);
  with TLoginController.Create(UserRepository, SmsSender) do
  try
    ForgotMyPassword('toto');
  finally
    Free;
  end;
end;

The method call to the mock/stub interface which is done before calling When captures the method/instance and Returns and ExpectsCount apply the expectation to this method/instance.
This should work down to Delphi 6 for any function with the matching overloads for When, just not for procedures.

Edit: changed the order of the calls

Last edited by Stefan (2012-11-23 12:51:02)

Offline

#11 2012-11-23 13:38:37

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

I still can't fully understand how you can let When(interface.method()) work as expected, from the grammatical point of view.

interface.method will e.g. return a string or a boolean - how may When() indentify the original method?
By setting a flag during the SmsSender.Send() fake execution, which may be retrieved by When()?
Could make sense... an idea to explore, I suspect.

But then, when the instances are injected to TLoginController, they should behave as expected.
For instance, all the internal journaling should be reset before execution.

Was it what you proposed?

Offline

#12 2012-11-23 13:41:28

Stefan
Member
Registered: 2012-11-23
Posts: 26

Re: Stubs and Mocks in mORMot

ab wrote:

By setting a flag during the SmsSender.Send() fake execution, which may be retrieved by When()?
Could make sense... an idea to explore, I suspect.

But then, when the instances are injected to TLoginController, they should behave as expected.
For instance, all the internal journaling should be reset before execution.

Was it what you proposed?

Exactly smile

Offline

#13 2012-11-23 14:35:36

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

So it would imply coding something like this:

procedure TMyTest.ForgotMyPassword;
var
  SmsSender: ISmsSender;
  UserRepository: IUserRepository;
begin
  TInterfaceStub.Create(TypeInfo(ISmsSender), SmsSender);
  When(SmsSender.Send()).Returns(True);
  TInterfaceMock.Create(TypeInfo(IUserRepository), UserRepository, Self);
  When(UserRepository.Save()).ExpectsCount(qoEqualTo, 1);
  StartExecution;
  with TLoginController.Create(UserRepository, SmsSender) do
  try
    ForgotMyPassword('toto');
  finally
    Free;
  end;
end;

Here When() and StartExecution may use some global threadvar context for proper execution.

My concern is that you can not just call SmsSender.Send() with no parameters...
Two possibilities:
- Make all parameters optional (may introduce issues);
- Force putting some parameters (code will inflate and be difficult to write).

How to resolve this?

Offline

#14 2012-11-23 15:08:25

Stefan
Member
Registered: 2012-11-23
Posts: 26

Re: Stubs and Mocks in mORMot

ab wrote:

My concern is that you can not just call SmsSender.Send() with no parameters...
Two possibilities:
- Make all parameters optional (may introduce issues);
- Force putting some parameters (code will inflate and be difficult to write).

How to resolve this?

Imo the benefit of having to pass the parameter is that you can easily define expectations for matching parameters.
If you don't care about parameters just say so (I called the method WhenCallingWithAnyArguments in my mocks) and pass dummy values (empty string, 0, nil, ...).

Offline

#15 2012-11-24 10:51:31

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

Honestly, I'm still not convinced by the parameters dummy values to be set.
Is it worth it?

Most tests (i.e. just stubbing a method without any parameters expectations) will be more difficult and verbose to write IMHO.

Offline

#16 2012-12-06 12:24:34

Stefan
Member
Registered: 2012-11-23
Posts: 26

Re: Stubs and Mocks in mORMot

ab wrote:

Honestly, I'm still not convinced by the parameters dummy values to be set.
Is it worth it?

Most tests (i.e. just stubbing a method without any parameters expectations) will be more difficult and verbose to write IMHO.

I think it depends on what you are doing ("Mocks aren't stubs")
and how restrictive and specific you want to use a mock and its expectations.

Offline

#17 2013-01-25 00:19:45

Martin Sedgewick
Member
Registered: 2013-01-23
Posts: 12

Re: Stubs and Mocks in mORMot

I am trying to use Mocks and Stubs and have run into a problem: interfaces as parameters.

I am using Delphi 6 and source code from 23rd January.

First I define 2 interfaces. Interface I2 has a property which is of type I1.

type
  I1 = interface( IInvokable )
       ['{FDF97D0F-9290-44C2-AD2B-48C87EF9F8EA}']
       end;

  I2 = interface( IInvokable )
       ['{904BC00C-2745-4C12-B09C-D841627BC7AF}']
       procedure Set_I( value: I1 );

       property I: I1 write Set_I;
       end;

I then create a Mock.

procedure DoTest;
var
  iii : I2;
begin
  TInterfaceMock.Create( TypeInfo( I2 ), iii, NIL );
end;

This breaks in the interface factory because TypeInfoToMethodValueType returns smvNone as the typeValue. Looking at this method, it does not recognise interfaces as a type.

          ValueType := TypeInfoToMethodValueType(TypeInfo);
          case ValueType of
          smvNone: begin
            case TypeInfo^.Kind of
            ...
            end;
            raise exception.....
         end;

I have read some of the blog and documentation and cannot see any reference to this. I am not sure what the code does and if this is something you dont need, so have not implemented or if there is a problem with implementing it.

Also, it is the same with a tkMethod Kind. As I also pass Event Handlers in interfaces.


Edit:
I have done some further looking into it and I see it is because you store information as JSON through the textwriter class. But you store instance pointer along with the classname. Is it possible to add a new method to this class to store interface pointer and interface type? Then send back the interface when method is called?

Sorry if this should be a new topic, if it should, please let me know

Last edited by Martin Sedgewick (2013-01-25 01:08:40)

Offline

#18 2013-01-25 08:23:10

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

Interface as parameters are indeed not handled. The same for methods/events.
Just as they are not available for interface-based services.

I do not see how we may do it for interface-based services easily.
How should we know how to implement serialization of the interface? Using its underneath object? Then instantiate it on the other side with the proper interface definition?
Could be done, but a lot of work.
For Client-Server access, the mORMot preference is to use either plain classes, either value objects (i.e. records).

For stubbing/mocking, we may add handling of tkMethod/tkInterface kind of parameters, then use a dedicated method to provide the expected instances.
I've added a "Feature Request" for it.
http://synopse.info/fossil/tktview/155d58506

What should be the default value for such interface? I suspect, just nil.

Thanks for the feedback.

Offline

#19 2013-01-25 09:05:37

Martin Sedgewick
Member
Registered: 2013-01-23
Posts: 12

Re: Stubs and Mocks in mORMot

Thank you Arnaud.

I would expect NIL if not assigned.

Offline

#20 2013-05-16 13:20:18

Stefan
Member
Registered: 2012-11-23
Posts: 26

Re: Stubs and Mocks in mORMot

Is there a way of using TInterfaceStub or TInterfaceObjectFake to create a proxy for an interface by just specifying the OnInvoke callback (just like you can do with TVirtualInterface)?

With TVirtualInterface I can create something like this (quick & dirty implementation):

  TVirtualInterface.Create(TypeInfo(IMyInterface),
    procedure(Method: TRttiMethod; const Args: TArray<TValue>; out Result: TValue)
    var
      LArgs: TArray<TValue>;
      i: Integer;
    begin
      SetLength(LArgs, Length(Args) - 1);
      for i := Low(LArgs) to High(LArgs) do
        LArgs[i] := Args[i + 1];
      Result := Method.Invoke(TValue.From<IInterface>(instance), LArgs);
    end).QueryInterface(IMyInterface, proxy);

Offline

#21 2013-05-16 14:29:45

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

Just look two messages upper in this forum thread.
http://synopse.info/forum/viewtopic.php?pid=6406#p6406

This point is on the roadmap.

Offline

#22 2013-08-16 11:17:58

RafaelPiccolo
Member
Registered: 2013-08-16
Posts: 1

Re: Stubs and Mocks in mORMot

Where are these classes TInterfaceMock and TInterfaceStub? I looked everywhere in the source code and I couldn't find them.

Offline

#23 2013-08-16 12:47:16

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,165
Website

Re: Stubs and Mocks in mORMot

In mORMot.pas this is stated everyhwhere.
smile

Offline

Board footer

Powered by FluxBB