NSubstitute – mock better

When following test-driven development and just writing unit tests in general, you’ll often need to mock certain behaviour out of scope of your current test. The object that you’re testing may have dependencies on other objects and these objects can be complicated. Mocking allows you to isolate the components of the object you want to test and simulate the behaviour of the objects it depends on.

There are a number of .NET mocking libraries in circulation:

  • Moq – by far the most commonly used, look at any GitHub .NET project and they’re likely using Moq
  • NSubstitute – my personal favourite and the subject of this blog
  • FakeItEasy

In this blog, I’m going to demonstrate how easy this is with NSubstitute — my mocking library of choice.

Let’s say we have the following UserService code:

    public class UserService : IUserService
    {
        private readonly IUserRepo userRepo;
        private readonly ILogger<UserService> log;


        public UserService(IUserRepo userRepo, ILogger<UserService> log)
        {
            this.userRepo = userRepo;
            this.log = log;
        }


        public User AddUser(User user)
        {
            var existingUser = userRepo.GetUser(user.Id);

            if (existingUser != null)
            {
                log.LogError($"User with id {user.Id} already exists");
                throw new Exception("User already exists");
            }

            var createdUser = userRepo.CreateUser(user);
            return createdUser;
        }
    }

Ignoring any potential quirks of the production code, let’s see what we have to do to test the AddUser method. The service takes in two parameters, IUserRepo and ILogger<UserService> we’re going to need to mock those.

I have a simple unit test project set up, and added the NSubstitute NuGet package:

dotnet add package NSubstitute --version 4.3.0

To create the mock objects all you need to do is the following:

        private UserService userService;
        private ILogger<UserService> logger;
        private IUserRepo userRepo;

        [SetUp]
        public void Setup()
        {
            logger = Substitute.For<ILogger<UserService>>();
            userRepo = Substitute.For<IUserRepo>();

            userService = new UserService(userRepo, logger);
        }

This is different to the likes of Moq where you’d have to create a Mock type that contains the mocked Object:

            IUserRepo userRepo = Substitute.For<IUserRepo>();  // NSubstitute
             
            Mock mockUserRepo = new Mock<IUserRepo>();         // Moq    
            IUserRepo userRepo = (IUserRepo) mockUserRepo.Object;

Now we’ve got these objects mocked let’s create a simple test to confirm a user gets created.

Note: if you’re confused by the assertions check out FluentAssertions

        [Test]
        public void AddUser_ShouldCreateUser_WhenNewUser()
        {
            // given
            var userId = Guid.NewGuid().ToString();
            var user = new User()
            {
                Email = "luke@gmail.com",
                Id = userId,
                GivenName = "Luke Garrigan"
            };

            userRepo.GetUser(userId).ReturnsNull(); // user doesnt exist
            userRepo.CreateUser(user).Returns(user);

            // when
            var result = userService.AddUser(user);

            // then
            result.Should().Be(user);
        }

The beauty here is in how we’re mocking the return values:

userRepo.GetUser(userId).ReturnsNull(); // user doesnt exist
userRepo.CreateUser(user).Returns(user);

NSubstitute is built using extension methods, it is a fluent API. The same code using Moq would look like the following:

userRepo.Setup(x => x.GetUser(userId)).Returns(null as User);
userRepo.Setup(x => x.CreateUser(user)).Returns(user);

Next, let’s create a test to ensure we throw an exception when there’s already a user:

        [Test]
        public void AddUser_ThrowAnException_WhenUserExists()
        {
            // given
            var userId = Guid.NewGuid().ToString();
            var user = new User()
            {
                Email = "luke@gmail.com",
                Id = userId,
                GivenName = "Luke Garrigan"
            };

            userRepo.GetUser(userId).Returns(user); // user exists

            // when
            Action act = () => userService.AddUser(user);
            
            // then
            act.Should().Throw<Exception>();
            logger.Received(1).LogError($"User with id {user.Id} already exists");
        }

So here we’re testing to ensure that the LogError message was called once with the correct error message:

logger.Received(1).LogError($"User with id {user.Id} already exists");

I’ve passed in the exact error message here, but you might not care about that:

 logger.Received(1).LogError(Arg.Any<string>());

Which is synonymous with Moq’s It.IsAny<string>().

That’s pretty much all there is to it. This isn’t a dig at Moq as I probably use it more than NSubstitute on a daily basis. This blog was more to hopefully open people’s eyes to other mocking implementations. I personally love the simplicity and readability of NSubstitute, and it’s my go-to for all personal projects. Let me know what mocking tools you use and why!

If you liked this blog then please sign up for my newsletter and join an awesome community!

Also, I currently run a completely free Planning Poker website that is gaining quite a lot of traction. If you work in a dev team and do biweekly sprints, give it a go and let me know if there’s anything you’d like added. You can also get involved, the code is completely open-source.

Leave a Reply