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.
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.
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
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
Let's spin up POSTMan and invoke
As shown in the screen shot, the imposter responded with a canned response.
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
The mock verification endpoint responds back with the resource invoked along with the total number of invocations so far.
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.