Reminder to self: write a longer article on Delphi mocking as Spring4D mocking is much better than Delphi Mocks, especially because of code-completion reasons.
Spring4D has a Mock
record that can return a Mock<T>
on which you can verify using methods like Received
.
See:
- http://docs.spring4d.org/index.htm?Spring.Mocking.Mock.htm
- http://docs.spring4d.org/index.htm?Spring.Mocking.Mock.From(T).htm
- http://docs.spring4d.org/index.htm?Spring.Mocking.Mock(T).htm
- https://stackoverflow.com/questions/30349058/how-to-mock-spring4d-events-with-dunit
I got some code that I need to dig up from an old project with many more Spring4D Mock examples.
Note that:
- Mocks can do much more than assist in unit testing; Spring4D uses them heavily for the container to do auto-mocking when setting up with
container.AddExtension<TAutoMockExtension>
: [WayBack] unit testing – How to Mock Spring4D Events with DUnit – Stack Overflow. - As always you need to be careful with your references in order to manage lifetime: [WayBack] Mocking interfaces in DUnit with Delphi-Mocks and Spring4D – Stack Overflow
For now, these are a start [WayBack] Delphi Unit Testing : Writing a simple spy for the CUT – Stack Overflow:
Sounds like a use case for a mock (I am using the term mock here because most frameworks refer to their various kinds of test doubles as mock)
In the following example I am using DUnit but it should not make any difference for DUnitX. I am also using the mocking feature from Spring4D 1.2 (I did not check if Delphi Mocks supports this)
unit MyClass; interface type TMyClass = class private fCounter: Integer; protected procedure MyProcedure; virtual; public property Counter: Integer read fCounter; end; implementation procedure TMyClass.MyProcedure; begin Inc(fCounter); end; end. program Tests; uses TestFramework, TestInsight.DUnit, Spring.Mocking, MyClass in 'MyClass.pas'; type TMyClass = class(MyClass.TMyClass) public // just to make it accessible for the test procedure MyProcedure; override; end; TMyTest = class(TTestCase) published procedure Test1; end; procedure TMyClass.MyProcedure; begin inherited; end; procedure TMyTest.Test1; var // the mock is getting auto initialized on its first use // and defaults to TMockBehavior.Dynamic which means it lets all calls happen m: Mock<TMyClass>; o: TMyClass; begin // set this to true to actually call the "real" method m.CallBase := True; // do something with o o := m; o.MyProcedure; // check if the expected call actually did happen m.Received(Times.Once).MyProcedure; // to prove that it actually did call the "real" method CheckEquals(1, o.Counter); end; begin RegisterTest(TMyTest.Suite); RunRegisteredTests(); end.
Keep in mind though that this only works for virtual methods.
–jeroen
$RTTI
directive, you can use a little trick and redefine it in subclass inpublic
section asoverride; abstract;
this will cause the RTTI to be generated. – Honza RFeb 5 ’16 at 8:02