Testing with Hoverfly and Java Part 3: State

Previously we simulated a delay scenario using Hoverfly. Now it’s time to dive deeper and go for a state based testing. By doing a stateful simulation we can change the way the tests endpoints behave based on how the state changed.

 

Hoverfly does have a state capability. State in a hoverfly simulation is like a map. Initially it is empty but you can define how it will get populated per request.

Our strategy would be to have a request that initializes the state and then specifies other requests that change that state.

public class SimulationStateTests {

	private Hoverfly hoverfly;

	@BeforeEach
	void setUp() {
		var simulation = SimulationSource.dsl(service("http://localhost:8085")
				.get("/initialize")
				.willReturn(success("{\"initialized\":true}", "application/json")
						.andSetState("shouldSucceed", "true")
				)
				.get("/state")
				.withState("shouldSucceed", "false")
				.willReturn(serverError().andSetState("shouldSucceed", "true"))
				.get("/state")
				.withState("shouldSucceed", "true")
				.willReturn(success("{\"username\":\"test-user\"}", "application/json")
						.andSetState("shouldSucceed", "false"))

		);

		var localConfig = HoverflyConfig.localConfigs().disableTlsVerification().asWebServer().proxyPort(8085);
		hoverfly = new Hoverfly(localConfig, SIMULATE);
		hoverfly.start();
		hoverfly.simulate(simulation);
	}

	@AfterEach
	void tearDown() {
		hoverfly.close();
	}

}

Unfortunately on the state we can only specify values in a key value fashion and not by passing a function for a key.
However with the right workaround many scenarios could be simulated.

In the example we first initialize the state and the we issue requests that behave differently based on the state, but also they do change the state.

So we expect to have a continuous first succeed and then fail mode, which can be depicted in the following test.

	@Test
	void testWithState() {
		var client = HttpClient.newHttpClient();
		var initializationRequest = HttpRequest.newBuilder()
				.uri(URI.create("http://localhost:8085/initialize"))
				.build();
		var initializationResponse = client.sendAsync(initializationRequest, HttpResponse.BodyHandlers.ofString())
				.thenApply(HttpResponse::body)
				.join();
		Assertions.assertEquals("{\"initialized\":true}", initializationResponse);

		var statefulRequest = HttpRequest.newBuilder()
				.uri(URI.create("http://localhost:8085/state"))
				.build();

		for (int i = 0; i < 100; i++) {
			var response = client.sendAsync(statefulRequest, HttpResponse.BodyHandlers.ofString())
					.join();

			int statusCode = i % 2 == 0 ? 200 : 500;

			Assertions.assertEquals(statusCode, response.statusCode());
		}
	}

That’s all about stateful simulation. On the next part we shall proceed on Hoverfly matchers

Testing with Hoverfly and Java Part 2: Delays

On the previous post we implemented json and Java based Hoverfly scenarios..
Now it’s time to dive deeper and use other Ηoverfly features.

A big part of testing has to do with negative scenarios. One of them is delays. Although we always mock a server and we are successful to reproduce erroneous scenarios one thing that is key to simulate in todays microservices driven world is delay.

So let me make a server with a 30 secs delay.

public class SimulateDelayTests {

	private Hoverfly hoverfly;

	@BeforeEach
	void setUp() {
		var simulation = SimulationSource.dsl(service("http://localhost:8085")
				.get("/delay")
				.willReturn(success("{\"username\":\"test-user\"}", "application/json").withDelay(30, TimeUnit.SECONDS)));

		var localConfig = HoverflyConfig.localConfigs().disableTlsVerification().asWebServer().proxyPort(8085);
		hoverfly = new Hoverfly(localConfig, SIMULATE);
		hoverfly.start();
		hoverfly.simulate(simulation);
	}

	@AfterEach
	void tearDown() {
		hoverfly.close();
	}

}

Let’s add the Delay Test

@Test
void testWithDelay() {
   var client = HttpClient.newHttpClient();
   var request = HttpRequest.newBuilder()
         .uri(URI.create("http://localhost:8085/delay"))
         .build();
   var start = Instant.now();
   var res = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
         .thenApply(HttpResponse::body)
         .join();
   var end = Instant.now();
   Assertions.assertEquals("{\"username\":\"test-user\"}", res);

   var seconds = Duration.between(start, end).getSeconds();
   Assertions.assertTrue(seconds >= 30);
}

Delay simulation is there, up and running, so let’s try to simulate timeouts.

	@Test
	void testTimeout() {
		var client = HttpClient.newHttpClient();
		var request = HttpRequest.newBuilder()
				.uri(URI.create("http://localhost:8085/delay"))
				.timeout(Duration.ofSeconds(10))
				.build();
		assertThrows(HttpTimeoutException.class, () -&gt; {
					try {
						client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).join();
					} catch (CompletionException ex) {
						throw ex.getCause();
					}
				}

		);
	}

That’s it we got delays and timeouts!
Other test scenarios should contain state which is covered on the next tutorial.