How do I mock a class that doesn't have virtual methods?
Let's assume you have a very well designed Delphi project, which respects the dependency injection and some other good practices.
Now let's assume you need to mock a class defined as:
TMyClass = class
public
procedure Method1;
procedure Method2
end;
Method1
and Method2
are not virtual. What do you do in this case? To mock an object we need to inherit it and override
every method you want to mock, but it is not possible in this case because they are not virtual
. Should I change the source code to add virtual
on every method I need to mock? Is not it bad?
Edit
I was thinking about creating a compiler directive to make all fields in the class to be virtual
, is it a good ideia? Only my test suite is going to set the compiler directive.
EDIT2开发者_JS百科*
Embarcadero should provide an easy way of changing a method pointer of a class to another method point, without the need for virtual
.
Make the methods virtual so you can mock them. (They don't need to be abstract.)
If you cannot do that, then wrap the class with another class. Make the wrapper's methods virtual, and in the default implementation, just forward call calls to the original class. Wherever your program uses the original class, replace it with the wrapper. Now, mock the wrapper.
To mock an object we need to inherit it, but it is not possible in this case.
I'd recommend that every class that you say "I need to mock this" should end up being based on an interface.
In other words, if you have methods that need to be mocked, they should be put into an interface, and then the class to be mocked implements that interface. Then, you create the mock by implementing the same interface in the mock object.
The alternative is to use a Mocking library. You can look at these SO question:
What is your favorite Delphi mocking library?
and this framework includes mocking objects as well:
http://code.google.com/p/emballo/
Mocking objects should also encourage the proper use of dependency injection in your code.
You should not only make the methods virtual, you should declare a pure virtual base class, and make the other classes use only the pure virtual base class name. Now you can use what we call the "Liskov substitution principle", and you can make as many concrete subtypes of the abstract base class as you need. Since the abstract base class works just like an Interface works in delphi, minus the reference counting part, you get the best of both worlds. You can really keep your app code simple, and reduce bad coupling this way, plus you get unit testable "composite objects" that you put together yourself.
// in UnitBase.pas
TMyClassBase = class
public
procedure Method1; virtual; abstract;
procedure Method2; virtual; abstract;
end;
// in UnitReal.pas
TMyClassReal = class(TMyClassbase)
public
procedure Method1; override;
procedure Method2; override;
end;
// in UnitMock.pas
TMyClassMock = class(TMyClassbase)
public
procedure Method1; override;
procedure Method2; override;
end;
In the place that uses "TMyClass", change it to use TMyClassbase:
TMyOtherClass = class(TMyOtherClassBase)
private
FMyThing:TMyClassbase;
public
property MyThing:TMyClassBase read FMyThing write FMyThing;
end;
By connecting TMyOtherclass at runtime you can decide whether to use the real or mock class:
// in my realapp.pas
MyOtherClassObj := TMyotherClass.Create;
MyOtherClassObj.MyThing := TMyOtherClassReal.Create; // real object
// in my unittest.pas
MyOtherClassObj := TMyotherClass.Create;
MyOtherClassObj.MyThing := TMyOtherClassMock.Create; // mock object
(Don't forget to make a way to free MyOtherClassObj.MyThing later, or you'll have a leak)
精彩评论