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 tutorial builds on example code shown in the Quickstart. You should be familiar with the currency conversion example presented there before reading this more in-depth tutorial.

So far we have an account service that can transfer money between bank accounts, converting the currency using rates obtained from a currency service. We'll build on this codebase to demonstrate further features of the NMock framework.

Defining Expectations on Properties

We've seen how to tell a mock object to expect methods with certain arguments, and we can also define expectations on properties through getters and setters. Suppose we're writing a web front-end to our account transfer service using the model-view-presenter pattern. The view will be an ASP.NET page, the model corresponds to domain objects like Account, and the presenter is the logic that glues everything together.

The page we're trying to build allows the user to enter a “from” account number, a “to” account number, and an amount to transfer. The view looks like this:

public interface ITransferFundsView
{
    string FromAccount { get; }
    string ToAccount { get; }
    double Amount { get; }
}

The properties FromAccount, ToAccount and Amount probably correspond to ASP.NET text fields, but that's not really too important—the view is an interface and shields the presenter from needing to know whether the UI is actually an ASP.NET page or a smart-client WinForms app. The job of the presenter is to handle user input from the view and coordinate business functionality to perform requested actions, then update the view with any results. Since we're transferring money between accounts, the presenter will need both the view and an account service:

public class TransferFundsPresenter
{
    private readonly ITransferFundsView view;
    private readonly IAccountService accountService;

    public TransferFundsPresenter(ITransferFundsView view, IAccountService accountService)
    {
        this.view = view;
        this.accountService = accountService;
    }
}

Now we've figured out how the presenter will look, we can write a test. Our SetUp() method will create a mock view and account service, and pass the mocks into a real presenter which we will use for the test.

[TestFixture]
public class TransferFundsPresenterTest
{
    private Mockery mocks;
    private ITransferFundsView mockView;
    private IAccountService mockAccountService;
    private TransferFundsPresenter presenter;

    [SetUp]
    public void SetUp()
    {
        mocks = new Mockery();
        mockView = mocks.NewMock<ITransferFundsView>();
        mockAccountService = mocks.NewMock<IAccountService>();
        presenter = new TransferFundsPresenter(mockView, mockAccountService);
    }

As an initial scenario let's consider what happens when the user clicks the “transfer funds” button. The presenter should query the view for the two account numbers and the amount to transfer, and then use the account service to actually do the work:

    [Test]
    public void ShouldQueryViewAndUseAccountServiceToTransferFunds()
    {
        Expect.Once.On(mockView).GetProperty("FromAccount").Will(Return.Value("1234"));
        Expect.Once.On(mockView).GetProperty("ToAccount").Will(Return.Value("9876"));
        Expect.Once.On(mockView).GetProperty("Amount").Will(Return.Value(200.00));

        Expect.Once.On(mockAccountService).Method("TransferFunds").With("1234", "9876", 200.00);

        presenter.TransferClicked();
        mocks.VerifyAllExpectationsHaveBeenMet();
    }

Here we're defining expectations on the mock view's properties, namely FromAccount, ToAccount and Amount. We then tell the mock account service to expect the TransferFunds() method to be called with arguments matching those retrieved from the view. Finally we call the presenter's TransferClicked() method to simulate the view delegating to the presenter when the user pushes the button.

To get the test to pass, we need to write the following implementation:

    public void TransferClicked()
    {
        string fromAccount = view.FromAccount;
        string toAccount = view.ToAccount;
        double amount = view.Amount;
        accountService.TransferFunds(fromAccount, toAccount, amount);
    }

The implementation retrieves the properties from the view and uses them to call the TransferFunds() method on the account service. The test passes, everything's good.

Dealing with Exceptions

Our transfer page is working well so far, but what about some rainy-day scenarios? Let's suppose the user enters an invalid account number. The only piece of the system that would know a number is invalid is the account service (it's the only thing accessing real data right now, the presenter and view certainly wouldn't know a particular number was invalid). One implementation might be for the account service to throw InvalidAccountException if a bad account number is supplied. The presenter should probably catch this exception and display some kind of error message to the user.

    [Test]
    public void IfAccountServiceThrowsInvalidAccountExceptionShouldDisplayMessageToUser()
    {
        Stub.On(mockView).GetProperty("FromAccount").Will(Return.Value("1234"));
        Stub.On(mockView).GetProperty("ToAccount").Will(Return.Value("BAD NUMBER"));
        Stub.On(mockView).GetProperty("Amount").Will(Return.Value(200.00));

        Expect.Once.On(mockAccountService).
            Method("TransferFunds").
            With("1234", "BAD NUMBER", 200.00).
            Will(Throw.Exception(new InvalidAccountException()));

        Expect.Once.On(mockView).SetProperty("DisplayInvalidAccountMessage").To(true);

        presenter.TransferClicked();
        mocks.VerifyAllExpectationsHaveBeenMet();
    }

Our test is doing a couple of things differently this time. Firstly, instead of using expectations for the properties on the view, we use a stub. A stub works just like an expectation but means “zero or more times.” In this case it means that if the mock view's FromAccount, ToAccount and Amount properties are accessed they will return “1234”, “BAD NUMBER” and 200.00, but if the properties aren't accessed we don't care and the test will still pass. Secondly, when we're setting up an expectation on the account service mock's TransferFunds() method, instead of specifying a return value we tell the mock to throw a new InvalidAccountException. When a piece of implementation code calls the mocked method, NMock will throw the exception for us.

Finally we expect that the DisplayInvalidAccountMessage property on the view will be set to true by the presenter, presumably displaying some kind of error message or dialog box to the user, informing them they have used an invalid account number. The view now looks like this:

public interface ITransferFundsView
{
    string FromAccount { get; }
    string ToAccount { get; }
    double Amount { get; }
    bool DisplayInvalidAccountMessage { set; }
}

If we run this test without changing the presenter's TransferClicked() method, the InvalidAccountException is thrown by the mock account service, not caught by our implementation, and shown as an error by NUnit. The fix is simple—wrap the call to TransferFunds() with a try-catch block, and set the property on the view if appropriate:

    public void TransferClicked()
    {
        string fromAccount = view.FromAccount;
        string toAccount = view.ToAccount;
        double amount = view.Amount;
        try
        {
            accountService.TransferFunds(fromAccount, toAccount, amount);
        }
        catch(InvalidAccountException)
        {
            view.DisplayInvalidAccountMessage = true;
        }
    }

The new test passes, and we didn't break the old one. Excellent news.

NMock's Throw.Exception syntax can be used to test how your application handles exceptions in lots of different ways. Here we've seen how to show an error message based on the exception, but we could also write a test that said “the account service should catch a RecordNotFoundException and re-throw it as InvalidAccountException.”

Now that you've followed the tutorial, you might be interested in our Cheat Sheet, a printable one-page guide to NMock syntax. You might also like to read about advanced concepts, including defining custom matchers and actions.

ThoughtWorks     SourceForge