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:
- Install node.js. At the time of writing v4 or higher is required.
- Install Mountebank using npm by running
npm install -g mountebank
in a command prompt. - 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:
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:
- Create the MountebankClient.
- Create an imposter for the protocol you are testing (http in this case) and give it a port.
- Add a stub to the imposter and configure it to behave for the thing you are testing.
- Submit the imposter.
- 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.