Mocks & Stubs with MounteBank

As software developers, we often find ourselves working on projects where the systems we need to integrate with, are also under development or not yet stable. This is because a large part of the internal or third-party dependencies depends on development teams that are still working on the implementation or the improvements that are required.
However, it is common that in the initial design stage there is already a draft definition of how an API will respond and interact before and after the calls are received.
In order to parallelize teamwork and make simultaneous developments, it is highly recommended to use mocks, setting up a pre-configured response server.

Make this simple and use Mountebank as a mock server

  • It is simple to install and run — it’s based on NodeJS.
    Allows running instances from docker (can be deployed to k8)
  • For complex cases, there is always the option of using JavaScript functions to create behaviors.
  • It has been around for long enough to add features and iron out some issues.

What is mountebank?

mountebank is the first open-source tool to provide cross-platform, multi-protocol test doubles over the wire. Simply point your application under test to mountebank instead of the real dependency, and test like you would with traditional stubs and mocks.

At the moment, the following protocols are supported:

  • http / https / tcp (text and binary)/smtp

Mountebank supports mock verification, stubbing with advanced predicates, JavaScript injection, and record-playback through proxying. It also supports extensions that allow you to build custom protocol implementations in the language of your choice.

1. Getting Started

Install:

npm install -g mountebank

The option above requires node.js of v6 or higher to already be installed, but mountebank does not believe in making you fiddle with a tech stack just to get up and running.

Run:

mb

By default, mountebank listens on port 2525, but that’s not the port that your imposters (test doubles) will listen on. To show a couple of different kinds of imposters, let’s create both an http imposter and a TCP one. We’ll use the curl command-line tool to call mountebank's api. The following command creates the http imposter, listening on port 4545, by POSTing to http://localhost:2525/imposters with the given body. The predicates are optional - if you don't include any, the stub always matches, and the response is always sent.

Reference repository https://gitlab.com/osv77ldo/mountebank

2. Organize Mountebank stubs as one file per API path.

While it is possible to define all API stubs in a single Mountebank file, it is much better to have a detailed approach to defining stubs. Each API path would have its own stub file in a directory structure reflecting the API paths. Within a file, we would provide helper implementations for multiple HTTP methods (GET, POST, PUT, DELETE…)
example: Get Pets

Mountebank works with .ejs files so you can load stubs from multiple files at startup via a config file that is passed via:

mb — configfile stubs.ejs

3. Set the default response

Mountebank silently swallows requests made to APIs for which no stubs exist and returns an HTTP status 200 with no content. Unfortunately, this default applies when calling a stub that doesn’t exist as well as one that doesn’t fulfill the stub predicates, e.g. calling using GET when only POST is supported

This default can be defined once in the stubs config file (stubs.ejs in the above example). It looks like this:

default response example

4. Define global API responses in one place and reuse them

For example, to ensure that all APIs except unauthenticated ones had an authorizations header, create a global stub.

/global
missingHeadersParametersStubs.js

This could then be defined so that all API calls except those explicitly excluded must have an authorization header:

{
“predicates”: [
{
“and”: [
{
“not”: {
“or”: [
{“equals”: {“method”: “POST”, “path”: “/protectedPath”}}
]
}
},
{
“exists”: { “headers”: {“Authorization”: false} }
}
]
}
],
“responses”: [
{
“is”:
{
“statusCode”: 401,
“headers”: { “Content-Type”: “application/json”},
“body”:
{
“desc”: “UnAuthorized”
}
}
}
]
}

5. Set a mock

On the stub files above listed you need to define the predicates to be mocked (needs to define paths and HTTP verbs)

{"predicates": [{"equals": {"path": "/pets","method": "GET"}}],"responses": [{"inject": "<%- stringify(filename, 'pets/GetPets.js') %>"}]}

inyecting the responses

function inject() {const retrievePetsResponseDirPath = require('path').dirname(__dirname) +'/../../../pets/responses';return require(retrievePetsResponseDirPath + '/getPets');}

Mocked Response

{"statusCode": 200,"headers": {"Content-Type": "application/json"},"body": [{"id": 0,"category": {"id": 0,"name": "string"},"name": "doggie","photoUrls": ["string"],"tags": [{"id": 0,"name": "string"}],"status": "available"},{"id": 1,"category": {"id": 0,"name": "string"},"name": "matt","photoUrls": ["string"],"tags": [{"id": 0,"name": "string"}],"status": "available"}]}

6. Start the server and testing locally

npm start

Testing with postman

Testing the global stub

Conclusion

Work with mountebank can improve our daily teamwork due is simple, light and easy to use.

If you enjoyed today’s brief overview of mountebank and want to learn more about it, read the examples on mountebank’s website. These examples will help you to learn and develop your own mock calls to make the development process easier for you and your team!