Using NSubstitute to Check if a Method is Called With a Particular Object

NSubstitute is a great library for mocking objects for Test Driven Development (TDD) in .NET.  I love their approach of throwing away the confusion around Dummies, Mocks, Stubs, Fakes, Spies and Test Doubles and just letting you get to work writing great tests.  They use a typical Arrange/Act/Assert pattern so it fits well with the most popular .NET testing libraries like MSTest, NUnit or xUnit (we use this one at Clearly Agile).

One valuable and really easy to write test with NSubstitute is validating that a particular method was called with a particular object.  For a quick example, let's assume we are designing a user service that needs to create an audit entry every time a new user is added.  We already have an existing IAuditService and that looks like the following:

public interface IAuditService
{
    void AddNewUser(User user, string message);
}

Even though we don't have an established IUserService, UserService or supporting classes, first let's define a test using xUnit:

[Fact]
public void AddUser_Should_Call_Audit_Service_With_Added_User()
{
    //Arrange
    var auditService = Substitute.For<IAuditService>();

    //I like to highlight values I am checking for later by placing them in well-named variables;
    var userNameToValidate = "rgrimes";
    var auditMessageToValidate = $"Added user {userNameToValidate}";

    var userInfo = new UserInfo(){
        UserName = userNameToValidate,
        FirstName = "Rick",
        LastName = "Grimes"
    };
    
    var sut = new UserService(auditService);
    
    //Act
    sut.AddUser(userInfo);
    
    //Assert: Here's the magic.  Note that we are actually asserting two things here....AND THAT IS OK!
    auditService.Received().AddNewUser(Arg.Is<User>(u => u.UserName == userNameToValidate), auditMessageToValidate);
}

At the moment, this will not compile until we build the supporting classes:

public class UserInfo
{
    public string UserName {get; set;}
    public string FirstName {get; set;}
    public string LastName {get; set;}
}

public interface IUserService
{
    void AddUser(UserInfo user);
}

public class UserService : IUserService
{
    public UserService(IAuditService auditService)
    {
    }
    
    public void AddUser(UserInfo user)
    {
    }
}

The pitfall here for developers that are new to Test Driven Development is that they start adding code to the UserService to satisfy the test.  DO NOT DO THIS!  Write just enough code here to get the project to compile and nothing more.  Then turn your focus back to your test.

Run the test and it should fail, which oddly enough is a great start!  Now, we can go back into the UserService and make the test pass.

public class UserService : IUserService
{
    private readonly IAuditService _auditService;

    public UserService(IAuditService auditService)
    {
        _auditService = auditService;
    }
    
    public void AddUser(UserInfo user)
    {
        var user = new User();
        _auditService.AddNewUser(user, "Added user");
    }
}

Ok, let's run this test again.  What happens?  IT FAILS AGAIN?!?! Why?  Well our test is validating that the audit service is being called with a certain username and message...we are not doing that in the UserService...yet.  So, let's update the UserService to satisfy the test:

public class UserService : IUserService
{
    private readonly IAuditService _auditService;

    public UserService(IAuditService auditService)
    {
        _auditService = auditService;
    }
    
    public void AddUser(UserInfo user)
    {
        var user = new User()
        {
            UserName = user.UserName,
            FirstName = user.FirstName,
            LastName = user.LastName
        };
        
        _auditService.AddNewUser(user, $"Added user {user.UserName}");
    }
}

Now run the test again.  IT PASSES!!!  If you wanted a bit more fine-grained reporting on your test failures you could separate the two assertions in the test:

//Tests that just user was passed to Audit Service 
auditService.Received().AddNewUser(
    Arg.Is<User>(u => u.UserName == userNameToValidate), 
    Args.Any<string>());

//Tests that user and message was passed to Audit Service
auditService.Received().AddNewUser(
    Arg.Is<User>(u => u.UserName == userNameToValidate),
    auditMessageToValidate);

You could also validate that the entire object is what you are expecting...to me this is a bit of overkill and might delve too far into the implementation rather then checking behavior.  But here's any example anyway :-)

auditService.Received().AddNewUser(Arg.Is<User>(u => 
    u.UserName == userNameToValidate
    && u.FirstName == "Rick"
    && u.LastName == "Grimes"), auditMessageToValidate);

Happy coding!!!