These days a major problem exists when it comes to testing code that has to do with various cloud services where test tools are not provided.
For example although you might have the tools for local Pub/Sub testing, including Docker images you might not have anything that can Mock BigQuery.
This causes an issue when it comes to the CI jobs, as testing is part of the requirements, however there might be blockers on testing with the actual service. The case is, you do need to cover all the pessimistic scenarios you need to be covered (for example timeouts).
And this is where Hoverfly can help.
Hoverfly is a lightweight, open source API simulation tool. Using Hoverfly, you can create realistic simulations of the APIs your application depends on
Our first examples will have to do with simulating just a web server. The first step is to add the Hoverfly dependency.
1 2 3 4 5 6 7 8 | <dependencies> <dependency> <groupId>io.specto</groupId> <artifactId>hoverfly-java</artifactId> <version>0.12.2</version> <scope>test</scope> </dependency> </dependencies> |
Instead of using the Hoverfly docker image we shall use the Java Library for some extra flexibility.
We got two options on configuring the Hoverfly simulation mode. One is through the Java dsl and the other one is through json.
Letβs cover both.
The example below uses the Java DSL. We spin up hoverfly on 8085 and load this configuration.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class SimulationJavaDSLTests { private Hoverfly hoverfly; @BeforeEach void setUp() { .get("/user") .willReturn(success("{\"username\":\"test-user\"}", "application/json"))); var localConfig = HoverflyConfig.localConfigs().disableTlsVerification().asWebServer().proxyPort(8085); hoverfly = new Hoverfly(localConfig, SIMULATE); hoverfly.start(); hoverfly.simulate(simulation); } @AfterEach void tearDown() { hoverfly.close(); } @Test void testHttpGet() { var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .build(); var res = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .join(); Assertions.assertEquals("{\"username\":\"test-user\"}",res); }} |
Now letβs do the same with Json. Instead of manually trying things with json we can make the code do the work for us.
1 2 3 4 5 6 | .get("/user") .willReturn(success("{\"username\":\"test-user\"}", "application/json")));var simulationStr = simulation.getSimulation()System.out.println(simulationStr); |
We can get the JSON generated by the Java DSL. The result would be like this.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | { "data": { "pairs": [ { "request": { "path": [ { "matcher": "exact", "value": "/user" } ], "method": [ { "matcher": "exact", "value": "GET" } ], "destination": [ { "matcher": "exact", "value": "localhost:8085" } ], "scheme": [ { "matcher": "exact", "value": "http" } ], "query": {}, "body": [ { "matcher": "exact", "value": "" } ], "headers": {}, "requiresState": {} }, "response": { "status": 200, "body": "{\"username\":\"test-user\"}", "encodedBody": false, "templated": true, "headers": { "Content-Type": [ "application/json" ] } } } ], "globalActions": { "delays": [] } }, "meta": { "schemaVersion": "v5" }} |
Letβs place this one on the resources folder of tests under the name simulation.json
And with some code changes we get exactly the same result.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class SimulationJsonTests { private Hoverfly hoverfly; @BeforeEach void setUp() { var simulationUrl = SimulationJsonTests.class.getClassLoader().getResource("simulation.json"); var simulation = SimulationSource.url(simulationUrl); var localConfig = HoverflyConfig.localConfigs().disableTlsVerification().asWebServer().proxyPort(8085); hoverfly = new Hoverfly(localConfig, SIMULATE); hoverfly.start(); hoverfly.simulate(simulation); } @AfterEach void tearDown() { hoverfly.close(); } @Test void testHttpGet() { var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder() .build(); var res = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .join(); Assertions.assertEquals("{\"username\":\"test-user\"}",res); }} |
Also sometimes there is the need of combining simulations regardless they json or Java ones. This can also be facilitated by loading more that one simulations.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @Test void testMixedConfiguration() { var simulationUrl = SimulationJsonTests.class.getClassLoader().getResource("simulation.json"); var jsonSimulation = SimulationSource.url(simulationUrl); .get("/admin") .willReturn(success("{\"username\":\"test-admin\"}", "application/json"))); hoverfly.simulate(jsonSimulation, javaSimulation); var client = HttpClient.newHttpClient(); var jsonConfigBasedRequest = HttpRequest.newBuilder() .build(); var userResponse = client.sendAsync(jsonConfigBasedRequest, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .join(); Assertions.assertEquals("{\"username\":\"test-user\"}",userResponse); var javaConfigBasedRequest = HttpRequest.newBuilder() .build(); var adminResponse = client.sendAsync(javaConfigBasedRequest, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .join(); Assertions.assertEquals("{\"username\":\"test-admin\"}",adminResponse); } |
Thatβs it, we are pretty setup to continues exploring Hoverfly and itβs capabilities.
Published on Java Code Geeks with permission by Emmanouil Gkatziouras, partner at our JCG program. See the original article here: Testing with Hoverfly and Java Part 1: Get started with Simulation Mode Opinions expressed by Java Code Geeks contributors are their own. |
Thank you!
We will contact you soon.
Emmanouil GkatziourasAugust 24th, 2020Last Updated: September 16th, 2020

This site uses Akismet to reduce spam. Learn how your comment data is processed.