Announcing NSubstitute

Tonight at Sydney ALT.NET Anthony Egerton announced NSubstitute, his mocking framework for .NET 3.5+. I’ve been contributing some code to NSubstitute and I like to blog, so I thought I’d take it upon myself to do the written version of the announcement. :)

This all started a few months ago during some pair programming where we started discussing how we would ideally like our mocking code to read. Anthony came up with some neat syntax ideas and we decided to start working on it as a hobby project in our spare time, the aim being to play around with a simple, succinct syntax for mocking, as well as to try out some different testing techiques and to learn a bit about DynamicProxy.

We liked the syntax so much we’ve started using it on work projects, and so we thought we’d put it out there in case anyone else was interested or has some ideas on how to improve it. It’s still a little rough around the edges (the exception message formatting needs lots of work, and it doesn’t support Mono or Silverlight yet), but it’s pretty usable in its current state and should be enough to give people an idea of what we’re aiming for.

NSubstitute examples

Say we have an ICalculator interface:

public interface ICalculator {
  int Add(int a, int b);
  string Mode { get; set; }
  event Action PoweringUp;
}

We can ask NSubstitute to create a substitute instance for this type. We could ask for a stub, mock, fake, spy, test double etc., but why bother when we just want to substitute an instance we have some control over?

var calculator = Substitute.For<ICalculator>();

Now we can tell our substitute to return a value for a call:

calculator.Add(1, 2).Returns(3);
Assert.That(calculator.Add(1, 2), Is.EqualTo(3));

We can check that our substitute received a call, and did not receive others:

calculator.Add(1, 2);
calculator.Received().Add(1, 2);     
calculator.DidNotReceive().Add(5, 7);

If our Received() assertion fails, NSubstitute tries to give us some help as to what the problem might be:

NSubstitute.Exceptions.CallNotReceivedException : Expected to receive call:
    Add(1, 2)
Actually received:
    Add(4, 5)
    Add(3, 4)

We can also work with properties using the Returns syntax we use for methods, or just stick with plain old property setters (for read/write properties):

calculator.Mode.Returns("DEC");
Assert.That(calculator.Mode, Is.EqualTo("DEC"));

calculator.Mode = "HEX";
Assert.That(calculator.Mode, Is.EqualTo("HEX"));

NSubstitute supports argument matching for setting return values and asserting a call was received:

calculator.Add(10, -5);
calculator.Received().Add(10, Arg.Any<int>());
calculator.Received().Add(10, Arg.Is<int>(x => x < 0));

We can use argument matching as well as passing a function to Returns() to get some more behaviour out of our substitute (possibly too much, but that’s your call):

calculator
  .Add(Arg.Any<int>(), Arg.Any<int>())
  .Returns(x => (int)x[0] + (int)x[1]);
Assert.That(calculator.Add(5, 10), Is.EqualTo(15));

Returns() can also be called with multiple arguments to set up a sequence of return values.

calculator.Mode.Returns("HEX", "DEC", "BIN");
Assert.That(calculator.Mode, Is.EqualTo("HEX")); 
Assert.That(calculator.Mode, Is.EqualTo("DEC")); 
Assert.That(calculator.Mode, Is.EqualTo("BIN")); 

Finally, we can raise events on our substitutes (unfortunately C# dramatically restricts the extent to which this syntax can be cleaned up):

bool eventWasRaised = false;
calculator.PoweringUp += () => eventWasRaised = true;

calculator.PoweringUp += Raise.Action();
Assert.That(eventWasRaised);

How can I try this out?

Easy, just head on over to nsubstitute.github.com and download the binary. Then add the NSubstitute.dll as a project reference, add using NSubstitute; to your CS file and start substituting. Or grab the source from the project site on GitHub and start tinkering.

If you try it out I’d love to hear how you go!

Some quick acknowledgements

It doesn’t seem right to plug this and not give kudos to some of the people that made it possible, both directly and indirectly.

First, Anthony deserves the credit for the awesome syntax ideas in NSub. I take full credit for all the implementation bugs, as well as for some of the ridiculous abstractions in the code (including a really dodgy, mini-IoC container for wiring up routes and handlers. Yuck. :)). Troy Hunt did a great job of knocking out a logo for us. Xerxes and Shannon also helped out with some code and suggestions respectively. Krzysztof also provided some much needed help over email and twitter on how to dynamically create delegates using .NET 3.5 expressions.

There are also a few people and projects that NSubstitute owes its existence to. Ayende and the awesome Rhino Mocks project really started us mocking in the first place, and did a great job making mocking viable on the .NET platform. Also thanks to Daniel Cazzulino and moq for bringing Arrange Act Assert-style mocking to .NET (to the world?). NSubstitute relies entirely on AAA mocking for its syntax, intentionally providing no support for strict mocks or record/replay. And finally to Castle Project’s DynamicProxy which we rely on (like almost everyone else) to generate our proxies.

 

Happy substituting! :)

Comments