There is no doubt that developing and testing in microservice architecture is difficult, due to many dependencies. This article describes how Hoverfly, the smallest service virtualisation tool out there, addresses a few problems you may encounter at work.
What problem did I have?
During the last project, my team and I worked in the microservice architecture. It divides the system into small services, which are not controlled and which may behave differently. We required one such service for development and testing purposes; unfortunately, it turned out to be unstable and even when it was up, we often experienced timeouts. Of course, this was unacceptable so we needed to find a solution to simulate or even replace the original service, which was causing the trouble. We wanted the solution to be easy to get familiar with, adopt, and maintain. Most importantly however, it had to be portable and easily transferable between developers’ machines, DEV, and QA environments. After conducting some research, we decided to choose Hoverfly – and we do not regret it!
Key Concepts: What is Hoverfly?
Let us briefly learn some theory about Hoverfly before I demonstrate it in practice. According to the documentation, Hoverfly is a lightweight, open source tool, which enables one to simulate API. For more information and the installation guide, please visit hoverfly.io. If you are a fan of virtualisation, Hoverfly was “dockerised” and may be easily found on Docker Hub spectolabs/hoverfly; in fact, this is how we used it.
The working concept of Hoverfly is really straightforward. In a nutshell, it is a proxy server that passes requests between the client and the server. See the diagram below.
As shown in the diagram, the capture and simulate modes are key features that help one understand how the tool works.
In capture mode, Hoverfly captures HTTP(S) traffic to create API simulations, which can be used in testing. In other words, it transparently records outgoing requests from the client and the incoming responses from the original API server. Hoverfly stores captured traffic as simulations in JSON files.
In simulate mode, each time Hoverfly receives a request from the application, instead of forwarding it to the original server, it serves the associated response from the internal cache or the previously prepared simulation file.
Hoverfly in action – basic use case
It is high time to check out everything I have described so far in action. Let us say there is a third-party service, which replies to requests very slowly. See below:
The response time takes over 30 seconds, which is unacceptable. To take advantage of Hoverfly, I start it and switch it to capture mode:
hoverctl start
hoverctl mode capture
From now on, Hoverfly will be running and listening on port 8500, which will be used for proxy purposes, and on port 8888, where the web admin console in available.
Now, to capture a response, it is enough to pass a request through the proxy, which makes the call:
curl --proxy http://localhost:8500 http://localhost:8080/slowResponseResources
The outcome is immediately visible on the web admin console found here: http://localhost:8888.
All the data needed for later simulation is collected in a simulation.json file:
{ "data": { "pairs": [ { "response": { "status": 200, "body": "["resource A","resource B","resource C"]", "encodedBody": false, "headers": { "Content-Type": [ "application/json;charset=UTF-8" ], "Date": [ "Fri, 24 Mar 2017 12:57:24 GMT" ], "Hoverfly": [ "Was-Here" ] } }, "request": { "requestType": "recording", "path": "/slowResponseResources", "method": "GET", "destination": "localhost:8080", "scheme": "http", "query": "", "body": "", "headers": { "Accept": [ "*/*" ], "Proxy-Connection": [ "Keep-Alive" ], "User-Agent": [ "curl/7.43.0" ] } } } ], "globalActions": { "delays": [] } }, "meta": { "schemaVersion": "v1", "hoverflyVersion": "v0.10.2", "timeExported": "2017-03-24T14:00:23+01:00" } }
Having completed the capture step, I can now switch Hoverfly to simulate mode and examine my REST service once again:
hoverctl mode simulate
curl --proxy http://localhost:8500 http://localhost:8080/slowResponseResources
I got the following result:
As seen on the screenshot above, the response time is 0,006 seconds, which is much better than the original service. Taking a quick look at the web console confirms that the simulation went well.
As the above example proves, replacing the original service with a Hoverfly proxy resolves my issue. The third-party service simulated in this way is efficient and pleasant to work with.
Hoverfly in action – advanced use case
Judging by the basic scenario I have just demonstrated, Hoverfly is suitable for more advanced things in our microservice architecture. In the next use case, I would like to apply the latest, most fashionable pattern in microservice architecture called ‘CircuitBreaker’ (see Circuit Breaker) to test it out in an integration manner. At first glance, the simulating network connection failure or health condition of microservice architecture seems to be easy but making it automated and customisable is not so trivial. In the context of Hoverfly, it turns out that, that might be easy to achieve by enriching Hoverfly with Middleware, which intercepts traffic between the client and the API (whether real or simulated), and allows one to manipulate it.
In this case, the Middleware I need should randomly return 200 or 503 response statuses, which reflect connection problems and timeouts. Additionally, I want to have this random generator working in a weight manner. It gives me the possibility to control relations between 200/503 status occurrences, which makes my script flexible and usable in many scenarios. For that purpose, I wrote the following Python script:
#!/usr/bin/env python import sys import json import logging import random from numpy.random import choice logging.basicConfig(filename='middleware.log', level=logging.DEBUG) logging.debug('Middleware "CircuitBreaker" called') def main(): payload = sys.stdin.readlines()[0] logging.debug(payload) payload_dict = json.loads(payload) payload_dict['response']['status'] = choice([200, 503], p=[0.1, 0.9]) if payload_dict['response']['status'] == 503: payload_dict["response"]["body"] = "Service Unavailable" print(json.dumps(payload_dict)) if __name__ == "__main__": main()
To run my script, I execute:
hoverctl start
hoverctl mode simulate
hoverctl middleware --binary python --script middleware.py
curl --proxy http://localhost:8500 http://localhost:8080/slowResponseResources
hoverctl stop
And using the Apache Benchmark Tool, I get:
As you can see, my third-party service for ten HTTP calls returns 503 responses in 90% of cases. It is determined by a weight in script set to 90%. This simulates a case when service is highly unreachable. The lower the weight, the more accessible the service.
Summary
I think Hoverfly is worth exploring. It is a powerful tool that offers far more features than I had the chance to try out.
- Adding delays to a simulation, (my first thought when I saw it – “Great for load testing!”)
- Capturing or simulating specific URLs
- Loose request matching using a Request Matcher
- And much more (see https://docs.hoverfly.io/en/latest/index.html)
To sum up my adventure with Hoverfly, I can confirm that it passed the exam in our project. Additionally, it is easy to learn and maintain during the whole development lifecycle. I strongly recommend using it in your next project – maybe it will become your best friend in API simulation.