Friday, October 27, 2006

Finally getting around to using NMock

I don't like to admit it, but I really am quite conservative. I get set in my ways and even if I hear about a cool new way to do stuff it often takes me a while to get around to trying it. A great example is NMock. I've known about NMock for probably a couple of years, but I've stubbornly stuck with coding my own mock objects rather than giving it a spin. That is until today when I finally gave in and downloaded NMock2. It's amazing when you think that I've been a champion of TDD in at least three organisations and I've given presentations and mentored people in TDD techniques, but I haven't investigated such a core tool for doing TDD. But then again I only started using Test Driven this year which I couldn't possibly imagine working without now.

NMock is a mock object framework. You use Mock objects and dependency injection in unit tests to allow you to test a single component rather than the whole stack. It's probably one of most important core concepts behind TDD. NMock is a really neat OO framework that leverages the powerfull .net reflection API to create concrete instances of interfaces at runtime. Say you've got an interface like this:

public interface IFoo
{
	int DoSomething(int id, string name);
}

And you've got a client class that uses IFoo to do something:

public class Client
{
	IFoo _foo;

	public Client(IFoo foo)
	{
		_foo = foo;
	}
	
	public int DoSomething(int id, string name)
	{
		return _foo.DoSomething(id, name);
	}
}

You can use NMock to create a mock object like this (I love the name 'Mockery':-):

Mockery mockery = new Mockery();
IFoo mockFoo = mockery.NewMock();

And then set expectations for your unit tests, so that when you run the test, if the correct parameters
aren't passed an exception is raised:

Expect.Once.On(mockFoo).Method("CreatePipelineAudit").With(9, "the name").Will(Return.Value(4));
Client client = new Client(mockFoo);
int result = client.DoSomething(9, "the name");
Assert.AreEqual(4, result);

One thing got me for about half an hour before I was saved by my colleague Preet is that you can't mix bare arguments with 'Is' parameters in the 'Will' clause. In the 'Will' clause you pass the argument values that you expect to be passed to your mock method as I've done above. But also you can use the very convenient 'Is' class that returns a 'Matcher'. What I didn't realise was that 'With' is overloaded:

IMatchSyntax With(params Matcher[] otherArgumentMatchers);
IMatchSyntax With(params object[] equalArgumentValues);

So you can't mix bare values with 'Is' arguments. So this wont work:

Expect.Once.On(mockFoo).Method("CreatePipelineAudit").With(9, Is.Anything).Will(Return.Value(4));

The only other thing that disapointed me about an otherwise excellent tool, is that you can't mock objects, only interfaces. The code I'm currently working on uses several abstract base classes and it would be really neat if NMock could provide mocks for them. I see that it's already a feature request, and that it was available in NMock 1.0. Let's hope they add it soon.

1 comment:

Andreas said...

Hello, thanks very much for a nice post about NMock 2. The thing that got you for half an hour (overloading Matcher) got me for a couple of hours more until I found your blog. :)

I was trying to use With() and a NameValueCollection as argument, but it seems like NMock doesn't support it. I got it to work by using Is.NotNull, couldn't find any type-checking Is function. You don't happen to know a solution?