I was a happy user of the Moq library for the last 12 years. I liked the simplicity of Moq and how it allowed me to write tests first and then incrementally build the functionality.
Unfortunately, the creator of Moq is currently doing his best to kill the project. As he declared, either his spyware SponsorLink strong-arms enough individual developers into sponsoring him, or he will quit developing Moq. This leaves us in a lose-lose situation and for me it is time to move my private projects to another mocking library.
I played with a few alternatives and finally settled with NSubstitute. It covers all my use cases and offers an even simpler syntax than what I know from Moq.
Installing NSubstitute
We can install NSubstitute into our test projects using the NuGet dialog from Visual Studio or with this command in the Package Manager Console:
1 |
NuGet\Install-Package NSubstitute -Version 5.0.0 |
I strongly suggest you read through the documentation at least once if you are new to NSubstitute. There you find everything you need to start with NSubstitute and the well-crafted examples are to the point and without any distractions.
Preparations
We use an interface (that we are going to mock), a class to store data and a service that we use as a stand in for our business classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System.Collections.Generic; namespace NSubstituteDemo.BusinessLogic { public interface IDataAccess { DemoDto GetById(int id); int Add(DemoDto dto); List<DemoDto> All(); bool IsConnectionReady(); } } |
1 2 3 4 5 6 7 8 9 |
namespace NSubstituteDemo.BusinessLogic { public class DemoDto { public int Id { get; set; } public string Name { get; set; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
using System.Collections.Generic; namespace NSubstituteDemo.BusinessLogic { public class DataService { private readonly IDataAccess dataAccess; public DataService(IDataAccess dataAccess) { this.dataAccess = dataAccess; } public DemoDto GetEntryById(int id) { var entry = this.dataAccess.GetById(id); return entry; } public int Add(DemoDto demoDto) { var newId = this.dataAccess.Add(demoDto); return newId; } public List<DemoDto> GetAllEntries() { var entries = this.dataAccess.All(); return entries; } public bool Check() { return this.dataAccess.IsConnectionReady(); } } } |
The method names of the service class are by design not an exact match to the ones in the interface. I did this to make it explicit that what we mock is a mock-implementation of the interface and not a change of the service class.
Mock the return value of a method
We can create a mock for our interface and define what values should be returned if this method gets called:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[Test] public void ReplaceReturnValues_NoParameters() { var demo1 = new DemoDto() { Id = 1, Name = "A" }; var demo2 = new DemoDto() { Id = 2, Name = "B" }; var mock = Substitute.For<IDataAccess>(); mock.All().Returns(new List<DemoDto>() { demo1, demo2 }); var sut = new DataService(mock); var result = sut.GetAllEntries(); Assert.AreEqual(2, result.Count); Assert.AreEqual("A", result.First().Name); Assert.AreEqual("B", result.Last().Name); } |
When we call the mocked method .All() through our service, we get the two instances back.
If our method requires parameters, but we do not care what those parameters are, we can use Arg.Any<int>() as a stand in. It does now not matter what Id we pass to the method and always get the same data back:
1 2 3 4 5 6 7 8 9 10 11 12 |
[Test] public void ReplaceReturnValues_IgnoreParameters() { var demo3 = new DemoDto() { Id = 3, Name = "C" }; var mock = Substitute.For<IDataAccess>(); mock.GetById(Arg.Any<int>()).Returns(demo3); var sut = new DataService(mock); var result = sut.GetEntryById(45); Assert.AreEqual(demo3, result); } |
Verify calls to a method
If we want to know if a method was called by our system under test, we can ask our mock with the Received() method for our expected number of method calls:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[Test] public void VerifyThatMethodWasCalled() { var demo4 = new DemoDto() { Id = 4, Name = "D" }; var mock = Substitute.For<IDataAccess>(); mock.GetById(Arg.Any<int>()).Returns(demo4); var sut = new DataService(mock); sut.GetEntryById(4); mock.Received(1).GetById(Arg.Any<int>()); mock.DidNotReceive().All(); } |
If the method gets called more often or less often than we specify, then our test will fail with this message:
NSubstitute.Exceptions.ReceivedCallsException : Expected to receive exactly 2 calls matching:
GetById(any Int32)
Actually received 1 matching call:
GetById(4)
The method DidNotReceive() helps us to make sure that a certain method was not called.
Throw exceptions on method calls
Sometimes we want to simulate an error state and throw an exception. We can do this in NSubstitute with the Throws() method:
1 2 3 4 5 6 7 8 9 10 |
[Test] public void ThrowExceptionWhenMethodIsCalled() { var mock = Substitute.For<IDataAccess>(); mock.GetById(Arg.Any<int>()).Throws(new ArgumentException()); var sut = new DataService(mock); Assert.Throws<ArgumentException>( () => sut.GetEntryById(4)); } |
Change return values between calls
If we need different return values between method calls, we can write those values as a comma separated list of parameters in the return() method:
1 2 3 4 5 6 7 8 9 10 11 |
[Test] public void ChangeReturnValuesBetweenMethodCalls() { var mock = Substitute.For<IDataAccess>(); mock.IsConnectionReady().Returns(false, false, true); var sut = new DataService(mock); Assert.IsFalse(sut.Check()); Assert.IsFalse(sut.Check()); Assert.IsTrue(sut.Check()); } |
Get the argument of a method call
If we need to know with which parameters our method was called, we can use this code to put them into a list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[Test] public void CollectParameterValues() { var entries = new List<DemoDto>(); var mock = Substitute.For<IDataAccess>(); mock.Add(Arg.Any<DemoDto>()) .Returns(1) .AndDoes(args => entries.Add(args.ArgAt<DemoDto>(0))); var sut = new DataService(mock); sut.Add(new DemoDto() { Id = 5, Name = "E" }); sut.Add(new DemoDto() { Id = 5, Name = "F" }); sut.Add(new DemoDto() { Id = 5, Name = "G" }); Assert.AreEqual(3, entries.Count); Assert.AreEqual("E", entries[0].Name); Assert.AreEqual("F", entries[1].Name); Assert.AreEqual("G", entries[2].Name); } |
We can use the same technique to replace values for the returned mocks on the fly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[Test] public void UseParameterValues() { var mock = Substitute.For<IDataAccess>(); mock.GetById(Arg.Any<int>()) .Returns(args => new DemoDto() { Id = args.ArgAt<int>(0), Name = "X" }); var sut = new DataService(mock); Assert.AreEqual(1, sut.GetEntryById(1).Id); Assert.AreEqual(2, sut.GetEntryById(2).Id); Assert.AreEqual(345, sut.GetEntryById(345).Id); } |
This little trick allows us to return an instance that has always a matching Id to the one that was requested in the parameter.
Alternatives
If you want to move away from Moq but prefer something else than NSubstitute, you should explore FakeItEasy. It offers more flexibility but needs a bit more knowledge to work effectively.
Conclusion
NSubstitute supports all my use-cases that I have when it comes to mocking. For many parts, I favour the syntax of NSubstitute over Moq. Should you want to switch to a mocking framework that focuses on mocking, I highly recommend you switch to NSubstitute.
I created an extension for Visual Studio that helps with the Moq Framework convention for NSubstitute. I hope it can help with a large part of this process.
https://marketplace.visualstudio.com/items?itemName=RicardoPignone.ricardopignone-Moq2NSubstitute
Hi Ricardo,
That looks like a great extension. Thanks!