|
From: Jim A. <JA...@th...> - 2004-01-18 21:47:05
|
You're probably all aware of NMock's inability to mock types without
default (empty) constructors. It's been bothering me for a while, given
that this trendy Inversion Of Control lark is driving us to create classes
with fatter constructors, so I decided to do something about it. I am
aware that one of the more important goals of mocking is to drive an
interface/implementation seperated design and that, arguably, expanding
the range of classes that we can mock subverts that goal. However, I
don't think many of us would argue that *every* concrete type should be
matched by an interface just for the sake of it, and we all work with
pre-defined classes that are not always described by interfaces.
Furthermore, creating an interface purely to mock a class is Evil. It is
a code smell (a tool smell, really, if you'll pardon the expression).
The code to allow NMock to support constructors turns out to be trivial,
but it does force the user to choose whether they want to pass parameters
through to the base class or not (yes, even though C# forces you to call a
base constructor, the CLR does not, so you would end up with a
non-initialised base). An example might describe it better. Given the
following class:
public class ClassWithFatConstructor
{
public int Foo;
public string Bar;
public ClassWithFatConstructor(int foo, string bar)
{
Foo = foo;
Bar = bar;
}
}
the following code would create a mock instance without calling the base
constructor:
IMock mock = new DynamicMock(typeof(ClassWithFatConstructor));
ClassWithFatConstructor instance =
(ClassWithFatConstructor)mock.GetInstance(false); //false indicates we
don't want to initialise the base class
We end up with a subclass of ClassWithFatConstructor, with all public
virtual methods mocked as usual, but with all fields in the base class
remaining in an uninitialised, or default state. This means that Foo == 0
and Bar == null, and that calls to unmocked methods which rely on that
state can potentially fail. The next snippet creates a mock instance and
ensures that it calls through to the base:
IMock mock = new DynamicMock(typeof(ClassWithFatConstructor));
ClassWithFatConstructor instance =
(ClassWithFatConstructor)mock.GetInstance(true, 123, "abc");
We now get a properly initialised base. There is one thing that still
bother me about the API though. There's no point passing parameters in if
we *don't* want to call the base constructor - we can just create a
default constructor on the mock and call that instead. So I'd appreciate
some feedback on how this would look.
Anyway, if you got this far, you probably have an opinion on whether this
should be in NMock or not, so let me know what you think and I'll either
post the code or shut up.
Cheers,
Jim |