Test doubles ; Over the wire
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
.
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.
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
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.