Stubbing REST services with Mountebank / MbDoNet

What is it?

Mountebank is an open source project that provides test doubles over multiple protocols i.e. http, https, tcp, smtp. MbDotNet is a .NET client library for interacting with Mountebank.

Why?

So why would you want to use Mountebank? The cases I am going to describe are when testing code that uses REST services. The purpose of Mountebank is to allow you to control very precisely how these services behave in tests.

This allows you to test your code that is calling the REST service with whatever scenario you want. For example, if you want to test what happens when the REST service returns a 404 Not Found or a 500 Internal Server error, or test that your code deserialises returned data correctly.

How?

Just to let you know, this is the first time I’ve used this but these are the basics that got me going.

Setting up Mountebank

You can follow the getting started guide but the steps are as follows:

  1. Install node.js. At the time of writing v4 or higher is required.
  2. Install Mountebank using npm by running npm install -g mountebank in a command prompt.
  3. Run Mountebank so that is can receive requests. Open a command prompt as an administrator and run mb.

Your command prompt should look something like this:

Capture

Leave this running while you’re testing.

Setting up MbDotNet

Next you’ll need to add a reference to MbDotNet using nuget. In a .NET command prompt, cd to the path of your test project and run dotnet add package MbDotNet. Alternatively add it in Visual Studio using the nuget package manager.

Creating tests

Now your ready to write some tests! I’m using XUnit for testing.

First you’ll need to setup a MountebankClient to configure the tests as follows:

public class CustomerServiceTests : IDisposable
{
    private readonly MountebankClient mountebankClient;
    public CustomerServiceTests()
    {
        mountebankClient = new MountebankClient();
        mountebankClient.DeleteAllImposters();
    }

    public void Dispose()
    {
        mountebankClient.DeleteAllImposters();
    }
}

Note the mountebankClient.DeleteAllImposters(). The reason for this is so that you can keep Mountebank running while running tests multiple times.

Now, suppose you have some code calling a REST service and you want to test what happens when a record with the Id you give it does not exist. This is where Mountebank comes in. This allows to say, when this url is called in this test, return this. For example:

[Fact]
public async Task Get_ShouldReturnNull_IfNotFound()
{
    //arrange
    var imposter = mountebankClient.CreateHttpImposter(4546);

    imposter.AddStub()
        .ReturnsStatus(HttpStatusCode.NotFound)
        .OnPathAndMethodEqual("/customers/123", Method.Get);

    mountebankClient.Submit(imposter);

    var customerService = new CustomerService("http://localhost:4546");

    //act
    var customer = await customerService.GetAsync(123);

    //assert
    Assert.Null(customer);
}

So the steps are:

  1. Create the MountebankClient.
  2. Create an imposter for the protocol you are testing (http in this case) and give it a port.
  3. Add a stub to the imposter and configure it to behave for the thing you are testing.
  4. Submit the imposter.
  5. Test your code that calls http in the same port as you set up the imposter.

Note that the code in CustomerService uses HttpClient to call a REST service.

Another example is testing the returned data is handled correctly. The following test ensures data is being deserialised into the Customer object correctly.

[Fact]
public async Task Get_ShouldReturnCustomer_IfFound()
{
    //arrange
    var imposter = mountebankClient.CreateHttpImposter(4546);

    imposter.AddStub()
        .ReturnsJson(HttpStatusCode.OK, new Customer { Name = "name", Website = "website" })
        .OnPathAndMethodEqual("/customers/123", Method.Get);

    mountebankClient.Submit(imposter);

    var customerService = new CustomerService("http://localhost:4546");

    //act
    var customer = await customerService.GetAsync(123);

    //assert
    Assert.NotNull(customer);
    Assert.Equal("name", customer.Name);
    Assert.Equal("website", customer.Website);
}

More Mountebank configuration examples can be be seen here in the MbDotNet github page.

As I said earlier, this is the first time I’ve used Mountebank / MbDotNet so I’m no expert. Comments always welcome!

Cheers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s