For a side project I’ve started to use NSubstitute which is a new mocking framework for .net. We currently use Moq for pretty much all of our projects but NSubstitute looks really nice so I thought I would try it out.
The syntax is really clean, with the call to Substitute.For<IThingToMock>() returning an IThingToMock. This means you can create your mock and then pass it in to your class you are testing without having to call a .Object like Moq does.
But where it really helped me out was with a particular situation. I’m dabbling with database migrations at the moment where I’m using a Migration attribute which I scan for. When scanning for these migration attributes I wanted to make sure that they were stored in order so in Moq I have this test:
var migrationStoreMock = new Mock<IMigrationStore>(); var storedMigrations = new List<IMigration>(); migrationStoreMock.Setup(s => s.StoreMigration(It.IsAny<IMigration>()).Callback<IMigration>(c => storedMigrations.Add(c)); var migrator = new Migrator(migrationStoreMock.Object); migrator.Scan<TypeThatHasTestMigrations>(); storedMigrations[0].Version.ShouldEqual(1); storedMigrations[1].Version.ShouldEqual(2); storedMigrations[2].Version.ShouldEqual(3); storedMigrations[3].Version.ShouldEqual(4);
What has always bugged me with this is that it ‘sort of’ follows the Arrange Act Assert pattern, but the callback to add to a list is really arranging ready for the asserts – so it really reads Arrange, Prepare For Assert, Act, Assert. Also, the assert isn’t on the mock itself, so there’s some indirection on what I’m asserting on.
Now for the NSubstitute equivalent:
var migrationStore = Substitute.For<IMigrationStore>(); var migrator = new Migrator(migrationStore); migrator.Scan<TypeThatHasTestMigrations>(); migrationStore.Received().StoreMigration(Arg.Is<IMigration>(x => x.Version == 1)); migrationStore.Received().StoreMigration(Arg.Is<IMigration>(x => x.Version == 2)); migrationStore.Received().StoreMigration(Arg.Is<IMigration>(x => x.Version == 3)); migrationStore.Received().StoreMigration(Arg.Is<IMigration>(x => x.Version == 4));
With this version there’s a much clearer Arrange Act Assert flow going on. The assert is on the mock itself and doesn’t involve a lambda call to achieve this (which is quite nice). The other benefit is that there’s no .Object call when passing the mock into the class under test.
What may seem noisy is the Arg.Is predicate – but that for me is ok. What’s more of an annoyance for me is I’ve lost the ShouldEqual.
But the fact that I can just call into the mock, through the Received extension method and then use it in a more natural way is very appealing.
It is early days for NSubstitute, with some of the messages it reports back needing work but that’s a known issue and for something in so early a stage it’s a very minor point – and one that I would love to contribute to resolving.
I love the syntax. Definitely better than the nested lambda expressions you get with AssertWasCalled(x=>x.Call(Arg.Matches(…))) in RhinoMocks.
By: Alun Harford on July 16, 2010
at 12:17 am
mock.Loaded += Raise.Action(); //Much nicer than RhinoMocks or Moq.
By: Alun Harford on July 16, 2010
at 12:26 am
Thanks for the feedback. 🙂
If you need to stick with the callback approach then NSub can also do this using the When extension method (or WhenForAnyArgs to ignore the specific arguments), but as you’ve pointed out the syntax isn’t as nice.
Definitely would welcome contributions. If you’d like to help polish up the exception messages that would be an awesome start! 🙂 Send me an email if you need any info, or post to the group (http://groups.google.com/group/nsubstitute).
Cheers,
David
By: David Tchepak on July 16, 2010
at 12:26 am
Moq, please for the love of God NO.Object();
By: Matt Davis on July 16, 2010
at 4:52 pm