NMock 2.0 is now the recommended version of NMock. If you're using an older version, please see the documentation for NMock 1.x.

This page is based on content originally written by Scott Ford for his blog, and by Joe Poon whilst researching NMock 2.0 advanced features. Many thanks to both of them for their original material and permission to reproduce it here.

Matchers

NMock2 comes with a matcher library. Like constraints in jMock, matchers specify expectations that can be used on mock objects and to perform assertions. You may not realize it but we have implicitly been using matchers on mock objects throughout this tutorial. Take a look at the following expectation.

Expect.Once.On(accountProvider).Method("Deposit").With(500);

Under the hood, NMock creates a MethodNameMatcher that expects the method named “Deposit” and a ComparisonMatcher (returned by Is.Equal()) to expect the input parameter 500. We could write the expectation like this instead:

Expect.Once.On(accountProvider).Method(new MethodNameMatcher("Deposit")).With(Is.EqualTo(500));

Essentially mock expectations are constructed with a series of matchers. Matchers can be built-in (provided by the NMock library) or custom (written by an end-user and shown in detail later in this tutorial). In most cases the built-in matchers provide everything you'll need for creating expectations. Some of the more common matchers are listed below.

Matchers found in the Is class

Matchers found in the Has class

In addition to using matchers to define expectations on mock objects, we can also use matchers to perform assertions in our test code. Suppose we have a test that loads a given account and verifies that its balance is within a specified range.

[Test]
public void AccountShouldHaveBalanceOfLessThan500Dollars()
{
    Account account = accountService.LoadAccount(12345);
    Assert.IsTrue((account.Balance >= 0) && (account.Balance <= 500));
}

The above test above is straightforward with no mock objects and just a single assertion. Everything is fine until the test fails. The failure message is limited at best:

NUnit.Framework.AssertionException

Is the balance less than 0 or greater than 500? To help us find out we can rewrite the test using the built-in NMock matchers.

[Test]
public void AccountShouldHaveBalanceOfLessThan500Dollars()
{
    Account account = accountService.LoadAccount(12345);
    Verify.That(account.Balance, Is.AtLeast(0) & Is.AtMost(500), "Account balance is not within expected range.");
}

The test now reads better, but the real benefit is found in the error message if the test fails. Here's the new output when our test fails because the account balance exceeds the upper boundary.

NMock2.Internal.ExpectationException: Account balance is not within expected range.
Expected: “? >= <0>” and “? <= <500>” Actual: <501>

Custom Matchers in NMock 2.0

Even though there are many matchers that are provided by NMock it is likely that these matchers will not always meet your needs. For instance, you may wish to check if a value matches a value in a collection or if a value satisfies a regular expression.

NMock makes it easy for us to write a custom matcher by extending the abstract Matcher class. Suppose we have a UserService that takes a list of users to add to a project. To prevent creating users that already exist, for each user in the supplied list, the service should ask the UserProvider whether or not this user already exists in the system. Once a list of verified new users is compiled, the user service should pass this list to the user provider to create the users. Let's look at the code.

[SetUp]
public void SetUp()
{
    Mockery mocks = new Mockery();
    IUserProvider userProvider = mocks.NewMock<IUserProvider>();
    IUserService userService = new UserService(userProvider);
}
[Test]
public void UserServiceShouldAskUserProviderWhichUsersAreNewAndRequestToAddThem()
{
    User newUser = new User("Charlie Brown", "charlie.brown@nmock.org");
    User existingUser = new User("John Smith", "john.smith@nmock.org");

    List allUsers = new List(new User[] { newUser, existingUser });
    List newUsers = new List(new User[] { newUser });

    Expect.Once.On(userProvider).Method("UserExists").With(newUser).Will(Return.Value(false));
    Expect.Once.On(userProvider).Method("UserExists").With(existingUser).Will(Return.Value(true));
    Expect.Once.On(userProvider).Method("CreateUsers").With(newUsers);

    userService.RegisterUsers(allUsers);
    mocks.VerifyAllExpectationsHaveBeenMet();
}

When we run the test we get the following error message:

NMock2.Internal.ExpectationException: unexpected invocation of userProvider.CreateUsers()

The error message is telling us there was an unexpected invocation of CreateUsers(), but in the test we did tell the mock to expect that method call. So what's going wrong? Let's look at what the user service is doing.

public void RegisterUsers(List users)
{
    List newUsers = new List();
    foreach(User user in users)
    {
        if (!provider.UserExists(user))
            newUsers.Add(user);
    }
    userProvider.CreateUsers(newUsers);
}

The CreateUsers() method queries the provider to see which users already exist and compiles a list of new users. It then passes the list of new users to the provider's CreateUsers() method. This seems to be the right implementation, so what's gone wrong? Why is our invocation of CreateUsers() “unexpected?”

The expectation on the mock user provider fails because, by default, NMock uses Equals() to match the arguments passed to the mock. The list of users created in the RegisterUsers() method is compared to the list we created in our test and NMock finds them to be different. Although the objects in the two lists might be equal and the implementation code is doing the right thing, NMock decides the two lists are different and the test fails. To fix this we need to compare the user objects in each list, rather than the lists themselves. We'll do this by creating a custom ListMatcher.

internal class ListMatcher : Matcher
{
    private IList list;

    public ListMatcher(IList accounts)
    {
        this.list = accounts;
    }

    public override bool Matches(object o)
    {
        if (!(o is IList)) return false;
        IList otherList = (IList)o;

        if (list.Count != otherList.Count) return false;
        for (int i = 0; i < list.Count; i++)
        {
            if (!list[i].Equals(otherList[i])) return false;
        }
        return true;
    }

    public override void DescribeTo(TextWriter writer)
    {
        writer.Write("List:");
        foreach (object o in list)
        {
            writer.Write(o.ToString() + " ");
        }
    }
}

The important thing to note is that the matcher derives from the abstract NMock class Matcher, and overrides the Matches() and DescribeTo() methods.

The Matches() method is where you put the logic that determines if the value that is passed in matches what the matcher is expecting. Our new list matcher checks that both objects are lists, that they contain the same number of items, and finally that each of the objects inside the list are equal to each other (using their Equals() methods). In this case we'd need to make sure User.Equals() was also correctly implemented.

The DescribeTo() method is used when NMock lists the expectations that have not been met. When writing your own matcher you should make sure that you put enough information in the DescribeTo() method that you can debug an unmet expectation.

To use this custom matcher in our test, we must first write a "syntax" class with a static factory method that creates our matcher and looks similar to the syntax of the factory methods in the Is class:

public class IsList
{
    public static Matcher Equal(IList otherList)
    {
        return new ListMatcher(otherList);
    }
}

We can now use our new matcher syntax in our expectation:

Expect.Once.On(userProvider).Method("RegisterUsers").With(IsList.Equal(newUsers));

Actions

NMock 2.0 actions are events that are triggered as the result of an expectation. Like matchers, you have been using NMock actions all along probably without realizing it. Below are two built-in NMock actions, Return and Throw.Exception.

Expect.Once.On(accountProvider).GetProperty("NumberOfActiveAccounts").Will(Return.Value(100));
Expect.Once.On(accountProvider).Method("Withdraw").Will(Throw.Exception(new Exception("You have no money in your account.")));

To see the actions let's look at what NMock is doing under the hood. NMock creates a new ReturnAction and ThrowAction which will return the specified value and throw the desired exception, respectively. We could re-write the expectations like this, but we wouldn't want to because it makes the tests harder to read:

Expect.Once.On(accountProvider).GetProperty("NumberOfActiveAccounts").Will(new ReturnAction(100));
Expect.Once.On(accountProvider).Method("Withdraw").Will(new ThrowAction(new Exception("You have no money in your account.")));

On most occasions the Return and Throw actions will accommodate your testing needs quite nicely. There may, however, be scenarios where you wish to implement a custom action, and NMock allows you to define custom Actions by implementing the IAction interface.

To demonstrate a custom action, let's think about creating a new account for a user. We'll use our AccountService to create a new account and then associate the account with a user. In a real environment the newly created account will be assigned an ID via the database upon insertion (the ID probably corresponds to a primary key in a database somewhere). To simulate this we can create a custom action named NewAccountAction which will fire and set the ID of the account. The custom action allows us to mimic the domain more closely. We will also create a syntax method for the new action to ensure that the test is as readable as possible.

[Test]
public void ShouldCreateNewSavingsAccountAndAssociateItWithUser()
{
    Account account = new Account(AccountType.Savings);
    User user = new User("charlie", "charlie.brown@nmock.org");
    int accountId = 999;

    Expect.Once.On(accountProvider).Method("CreateAccount").With(account).Will(SetAccountId(accountId));
    Expect.Once.On(userProvider).Method("AssociateAccountWithUser").With(accountId, user);

    accountService.CreateAccount(account, user);
    mocks.VerifyAllExpectationsHaveBeenMet();
}

private static Action SetAccountId(int id) {
    return new SetAccountIdAction(id);
}
internal class SetAccountIdAction : IAction
{
    private int accountId;

    public SetAccountIdAction(int accountId)
    {
        this.accountId = accountId;
    }

    public void Invoke(Invocation invocation)
    {
        Account account = (Account)invocation.Parameters[0];
        account.Id = accountId
    }

    public void DescribeTo(TextWriter writer)
    {
        writer.Write("Setting new Account ID to: " + accountId);
    }
}
ThoughtWorks     SourceForge