Mocks: The Next Generation

10/01/08

Mocks: The Next Generation

I recently wrote about my frustration with Rhino Mocks 3.

Rhino Mocks v3.5 (currently a release candidate) is a different animal. I’ve been writing learning tests against it, and it’s definitely an improvement.

Initially I tried the version targeting .NET 2.0. Even after posting on the forum, I was unable to get it to work with the new AAA syntax.

Once I switched to the .NET 3.5 version (our software at work has to target 2.0, but our unit tests can target 3.5), my experience improved. The documentation is still poor, but I was able to figure most of it out after a post or two.

The AAA syntax (Arrange, Act, Assert) is the key change. You no longer have to create a mock repository or switch to replay mode (unless you want to). Lambdas and extension methods make for a very clean syntax:

[Test]
public void MethodStub()
{
  IMyThings things = MockRepository.GenerateStub<IMyThings>();
  things.Stub(x => x.GiveMeOneMore(5)).Return(6);
  Assert.That(things.GiveMeOneMore(5), Is.EqualTo(6));
}

Stubs are what I use most of the time. There’s an inconvenience in the release candidate; you have to add .Repeat.Any() even on read-only properties if you want the result to be returned more than once. Ayende (the author of Rhino Mocks) wrote that this will be changed in the release version.

Using Do() is not at all obvious; I finally figured it out:

public interface IMyThings
{
  int GiveMeOneMore(int aValue);
}

// ...

[Test]
public void MethodStubReturnBasedOnParameter()
{
  IMyThings things = MockRepository.GenerateStub<IMyThings>();

  things.Stub(x => x.GiveMeOneMore(0))
    .IgnoreArguments()
    .Do((Func<int, int>)(arg => arg + 1))
    .Repeat.Any();

  Assert.That(things.GiveMeOneMore(98), Is.EqualTo(99));
  Assert.That(things.GiveMeOneMore(99), Is.EqualTo(100));
}

Unfortunately, changing a stubbed return value later is still tricky. You have to call BackToRecord(), change it, then call Replay(). For more details, see the thread I started on the forum.

Why would I want to do this? Daniel Cazzulino said it best: “In typical unit tests, your reusable objects live in test class fields that you initialize at test setup and cleanup on tear down. It’s quite common to have default behavior associated with mocks, and being able to reuse these expectations can make your tests much more concise…”

He’s also one of the developers behind Moq, another mocking framework. I liked his philosophy, so I wrote some more tests against Moq.

Although Moq’s philosophy is more in line with mine (classic TDD, stubs) and its documentation is worlds better, I have to say I prefer Rhino’s syntax.

Moq:

  var thing = new Mock<ISomeThing>();
  thing.ExpectGet(x => x.Number).Returns(21);
  Assert.That(thing.Object.Number, Is.EqualTo(21));

Rhino:

  ISomeThing thing = MockRepository.GenerateStub<ISomeThing>();
  thing.Number = 21;
  Assert.That(thing.Number, Is.EqualTo(21));

In other words, Moq returns a wrapper instead of an interface (you have to call .Object to get the implementing object). Rhino cleverly uses extension methods to allow setting expectations on the interface, and supports direct property sets for read/write properties. (It’s about the same as Moq if it’s a read-only property, unfortunately, and those are very common in my code.)

To ignore arguments, you also have to write (Moq):

  things.Expect(x => x.GiveMeOneMore(It.IsAny<int>()))
    .Returns(6);

rather than Rhino’s clearer:

  things.Stub(x => x.GiveMeOneMore(0)).IgnoreArguments()
    .Return(6);

Moq does not appear to support targeting .NET 2.0 at all.

There’s an excellent article comparing the frameworks at:
http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx

Long story short, I decided not to switch to Moq at this time. Rhino 3.5 does most of what I want, and I can live with its shortcomings. I hope that a future release of Rhino Mocks will allow developers to treat stubs as mutable, malleable objects that can be changed at will. I also hope that the documentation will improve; the learning curve is steeper than it needs to be right now.

Update - October 3, 2008

I ran into some issues with the Rhino Mocks release candidate, so I’m holding off on the upgrade for now. See http://groups.google.com/group/RhinoMocks/browse_thread/thread/ce6f6a3dd7fd9a9f

Update - October 5, 2008

See Mocks: The Next Generation II.


Your Host: webmaster@truewill.net
Copyright © 2000-2013 by William Sorensen. All rights reserved.