Please note: If the code snippets in this article are not visible on your mobile browser then try to view this article with Desktop site option enabled.

Developing microservices is complex and testing its integration is even more complex. Assuring availability of all the inter-dependent services with up-to-date builds in the test environment is quite a tedious & costly setup to maintain. Stubs and mocks on the other hand are cheap, easily maintainable and they make integration tests predictable and repeatable.

Almost two years ago, we faced a problem at work where we were desperately looking for a solution to test multiple Web API interactions with an external system over HTTP. In short, we wanted a hassle free mechanism to stub and verify invocation of an external service.

We researched and came across Mountebank, which was not only painful to configure, but was error prone too. To top it all off, configuring dynamic behavior required enabling javascript injection. No points for guessing, the security team of my company outrightly rejected the request to enable javascript injection on any of our servers. Not to mention, no clean way to deploy and integrate it with our CI/CD pipelines. Later, due to various constraints, we decided to spend our time on delivering new features instead of solving this problem.

Feeling dejected, I started imposter project in my free time with an intent to develop a simple, developer friendly stubbing and mocking mechanism. The project is now in a decent enough state to deserve a blog article.

Without further ado, in this article I will demonstrate how to create and configure stubs & mocks using the imposter project. Let's begin with configuring and creating stubs.

Configuring & creating stubs

To configure a stub, you need to reference FluentImposter.AspNetCore package. This package contains an ASP.Net middleware UseStubImposters, to configure stubs. All you need to do is to configure it in the Startup.cs as shown below:

ImpostersAsStubConfiguration, as the name suggests, accepts a list of imposters-as-stubs. These imposters are hosted as REST endpoints by the middleware. To demonstrate, let's create an imposter which will stub the Orders REST endpoint.

Being a fan of fluent APIs, I made the ImposterDefinition fluent. I like the idea of an API guiding me about the next steps. To begin with, it guides to define a resource to be stubbed, followed by a set of conditions that must match for the imposter to accept a request and respond with a canned response. In this example, if the request body contains Product:1234 then this imposter will return a pre-configured response.

Creating Pre-configured responses.

The ResponseBuilder helps to build the canned response. The code is pretty straight forward.

As shown in the below POSTMan screenshot, the Orders imposter endpoint is responding with a preconfigured response and a Created (201) status code because the request body has the content Product:1234.

The screen shot is of PostMan invoking Orders imposter and receiving a pre-configured response.

Let's move on to configure and create an imposter as a mock.

Configuring & creating mocks

The same package FluentImposter.AspNetCore package has another middleware UseMockImposters, which is capable of configure and hosting mocks. Example shown below:

Mocking requires invocation verification, hence a backing store is needed to keep the count of REST endpoint invocations. At the time of writing this article, I've only added support for Amazon DynamoDb as a backing store which can be enable by adding
Appify.FluentImposter.DataStore.AwsDynamoDb Nuget package in your project. In case you do not have an Amazon AWS account, you can work with local DynamoDb installation as well. But be aware that local DynamoDb, as the name suggests, is good for local testing and not recommended for production use.

Below is how you can create a MocksDatStore which connects to local DynamoDb installation.

I wouldn't go into the details of this datastore. All that the MocksDataStore should do is to return AwsDynamoDbDataStore instance which does all the magic of maintaining request invocations.

The next step is to create Imposters for the REST endpoints. To demonstrate, lets create a mock for api/Products endpoint.

Let's spin up POSTMan and invoke api/Products endpoint.

As shown in the screen shot, the imposter responded with a canned response.

POSTMan screenshot of invoking api/Products endpoint.

Mock verification

The UseMockImposters middleware hosts a mock verification endpoint with resource path mocks/verify accepting Http GET requests. The request body should contain the details of the REST endpoint for which invocation verification has to be done. Below is how the invocation verification for api/Products endpoint can be done using POSTMan

Verification of invocation using POSTMan

The mock verification endpoint responds back with the resource invoked along with the total number of invocations so far.

Conclusion

Thanks for reading. I am actively working on this project from last few months and planning to support more protocols. If you have any suggestion or improvements for the project then do let me know via comments. If you want to contribute then feel free to create pull requests in github.