Stubs and Mocks for Delphi with mORMot
By A.Bouchez on 2012, Sunday October 14, 13:31 - mORMot Framework - Permalink
Our mORMot framework is now 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.
If you want to follow the "test spy" pattern (i.e. no expectation defined a priori, but manual check after the execution), you can use:
procedure TMyTest.ForgotMyPassword;
var SmsSender: ISmsSender;
UserRepository: IUserRepository;
Spy: TInterfaceMockSpy;
begin
TInterfaceStub.Create(TypeInfo(ISmsSender),SmsSender).
Returns('Send',[true]);
Spy := TInterfaceMockSpy.Create(TypeInfo(IUserRepository),UserRepository,self);
with TLoginController.Create(UserRepository,SmsSender) do
try
ForgotMyPassword('toto');
finally
Free;
end;
Spy.Verify('Save');
end;
This is something unique with our library: you can decide if you want to use
the classic "expect-run-verify" pattern, or the somewhat more direct
"run-verify" / "test spy" pattern.
With mORMot, you pick up your mocking class (either
TInterfaceMock or TInterfaceMockSpy), then use it as
intended. You can even mix the two aspects in the same instance! It is just a
matter of taste and opportunity for you to use the right pattern.
For another easier pattern, like the one in the Mockito home page:
TInterfaceMock.Create(TypeInfo(ICalculator),ICalc,self).
ExpectsCount('Multiply',qoEqualTo,1).
ExpectsCount('Add',[10,20],qoEqualTo,1);
ICalc.Add(10,20);
ICalc.Multiply(10,30)
If you want to follow the "test spy" pattern, you can use:
Mock := TInterfaceMockSpy.Create(TypeInfo(ICalculator),ICalc,self);
ICalc.Add(10,20);
ICalc.Multiply(10,30)
Mock.Verify('Add');
Mock.Verify('Multiply',[10,30]);
If you compare with existing mocking frameworks, even in other languages / platforms like the two above, you will find out that the features included in mORMot are quite complete:
- Stubbing of any method, returning default values for results;
- Definition of the stubbed behavior via a simple fluent interface, with
TInterfaceStub.Returns(), including easy definition of returned results values, for the whole method or following parameters/arguments matchers; - Handle methods with
var, outor function result returned values - i.e. not only the function result (as other Delphi implementations does, due to a limitation of theTVirtualInterfacestandard implementation, on which mORMot does not rely), but all outgoing values, as an array of values; - Stubbed methods can use delegates or event callbacks with
TInterfaceStub.Executes()rule definitions, for the whole method or following parameters/arguments matchers, to run a more complex process; - Stubbed methods can also raise exceptions with
TInterfaceStub.Raises()rule definitions, for the whole method or following parameters/arguments matchers, if this is the behavior to be tested; - Clear distinction between
mocks and stubs, with two dedicated classes, named
TInterfaceStubandTInterfaceMock; - Mocks are directly linked to mORMot's unitary tests / test-driven classes;
- Mocked methods can trigger test case failure with
TInterfaceMock.Fails()definitions, for the whole method or following parameters/arguments matchers; - Mocking via "expect-run-verify" or "run-verify" (aka "test spy") patterns, on choice, depending on your testing expectations;
- Mocking validation against number of execution of a method, or a method
with arguments/parameters matchers, or the global execution trace - in this
case, pass count can be compared with operators like
< <= = <> > >=and not only the classic exact-number-of-times and at-least-once verification; - Most common parameters and results definitions can be defined as simple
array of constin the Delphi code, or by supplying JSON arrays (needed e.g. for more complex structures likerecordvalues); - Execution trace retrieval in easy to read or write text format (and not via
complex "fluent" interface e.g. with
Whenclauses); - Auto-release of the
TInterfaceStub TInterfaceMock TInterfaceMockSpygenerator instance, when the interface is no longer required, to minimize the code to type, and avoid potential memory leaks; - Works from Delphi 6 up to XE3 - since no use of syntax sugar like generics,
nor the
RTTI.pasfeatures; - Very good performance (the faster Delphi mocking framework, for sure), due
to very low overhead and its reuse of mORMot's low-level
interface-based services kernel using JSON serialization, which does not rely
on the slow and limited
TVirtualInterface.
This article is part of a list of mocking/stubbing range of information:
- Interfaces in practice: dependency injection, stubs and mocks
- Stubs and Mocks for Delphi with mORMot;
- Advanced mocks and stubs;
Feedback is welcome on our forum, just as usual.