Core Java

Testing with Hoverfly and Java Part 1: Get started with Simulation Mode

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() {
        var simulation = SimulationSource.dsl(service("http://localhost:8085")
                .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()
                .uri(URI.create("http://localhost:8085/user"))
                .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
var simulation = SimulationSource.dsl(service("http://localhost:8085")
            .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()
                .uri(URI.create("http://localhost:8085/user"))
                .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);
 
 
        var javaSimulation = SimulationSource.dsl(service("http://localhost:8085")
                .get("/admin")
                .willReturn(success("{\"username\":\"test-admin\"}", "application/json")));
 
        hoverfly.simulate(jsonSimulation, javaSimulation);
 
        var client = HttpClient.newHttpClient();
        var jsonConfigBasedRequest = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:8085/user"))
                .build();
        var userResponse = client.sendAsync(jsonConfigBasedRequest, HttpResponse.BodyHandlers.ofString())
                .thenApply(HttpResponse::body)
                .join();
        Assertions.assertEquals("{\"username\":\"test-user\"}",userResponse);
 
        var javaConfigBasedRequest = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:8085/admin"))
                .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.

Emmanouil Gkatziouras

He is a versatile software engineer with experience in a wide variety of applications/services.He is enthusiastic about new projects, embracing new technologies, and getting to know people in the field of software.
Subscribe
Notify of
guest

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

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button