Simulate Network Failures in a Spring Boot Application

Bubu Tripathy
5 min readMar 22, 2023

--

Introduction

WireMock is a powerful and flexible tool for simulating HTTP-based APIs. One of its key features is the ability to simulate different kinds of network failures, such as timeouts and connection errors, which can be useful for testing how your application handles unexpected network behavior.

In this tutorial, we’ll walk through the process of setting up WireMock to simulate network failures for a Spring Boot application.

Setup a basic Spring Boot application

To get started, we’ll create a basic Spring Boot application that we can use to test our WireMock setup. Here’s the pom.xml file for our project:

<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>wiremock-example</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<java.version>11</java.version>
<spring-boot.version>2.6.3</spring-boot.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

This pom.xml file includes the spring-boot-starter-web dependency, which provides the basic functionality we need for our Spring Boot application. Add the WireMock dependency to the Maven project.

<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>2.27.2</version>
</dependency>

Next, let’s create a simple REST controller that returns a greeting message. Here’s the code for our controller:

@RestController
public class GreetingController {
@GetMapping("/greeting")
public String greeting() {
return "Hello, world!";
}
}

This code defines a simple REST endpoint at /greeting that returns a greeting message. Finally, let’s add a main method that starts our Spring Boot application. Here's the code for our Application class:

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Configure WireMock to simulate network failures

Now that we have WireMock added as a dependency, let’s configure it to simulate network failures. To do this, we’ll need to create a new test class and add some code to set up our WireMock server. Here’s the code for our test class:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class NetworkFailureTest {
@Autowired
private TestRestTemplate restTemplate;

@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig()
.dynamicPort()
.networkLatency(1000, TimeUnit.MILLISECONDS)
.dropConnectionPercentage(50));

@Test
public void testGreeting() {
String response = restTemplate.getForObject("/greeting", String.class);
assertEquals("Hello, world!", response);
}
}

Let’s walk through this code line by line. First, we use the @RunWith annotation to specify that we want to run our test with the SpringRunner class. Next, we use the @SpringBootTest annotation to tell Spring Boot to start up our application in a random port. We also autowire a TestRestTemplate object, which we'll use to make requests to our application.

Then, we define a WireMockRule object using the WireMockConfig class. This object sets up our WireMock server and configures it to simulate a network latency of 1000 milliseconds and a 50% chance of dropping connections.

Finally, we write a simple test that makes a request to our /greeting endpoint and verifies that the response is "Hello, world!".

Verify application behavior

Now that we have our WireMock server set up to simulate network failures, let’s write some tests to verify that our application behaves correctly when it encounters these failures. Here’s the code for our updated test class:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class NetworkFailureTest {
@Autowired
private TestRestTemplate restTemplate;

@Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig()
.dynamicPort()
.networkLatency(1000, TimeUnit.MILLISECONDS)
.dropConnectionPercentage(50));

@Test
public void testGreeting() {
String response = restTemplate.getForObject("/greeting", String.class);
assertEquals("Hello, world!", response);
}

@Test
public void testTimeout() {
wireMockRule.stubFor(get(urlEqualTo("/greeting"))
.willReturn(aResponse()
.withStatus(200)
.withFixedDelay(2000)));

assertThrows(RestClientException.class, () -> {
restTemplate.getForObject("/greeting", String.class);
});
}

@Test
public void testConnectionError() {
wireMockRule.shutdownServer();

assertThrows(RestClientException.class, () -> {
restTemplate.getForObject("/greeting", String.class);
});
}
}

Let’s walk through this code line by line. First, we have the same setup code as before. Next, we add two new test methods: testTimeout and testConnectionError. The testTimeout method configures WireMock to return a 200 status code with a fixed delay of 2000 milliseconds when it receives a request to /greeting.

This simulates a network timeout, since the response will take longer than the default timeout of our TestRestTemplate. We then use assertThrows to verify that our application throws a RestClientException when it encounters the timeout.

The testConnectionError method shuts down the WireMock server to simulate a network connection error. We then use assertThrows to verify that our application throws a RestClientException when it encounters the connection error.

Simulate Network Latency

One common type of network failure is increased latency, which can occur when there’s high network congestion or poor network connectivity. You can use WireMock to simulate network latency by adding a delay to the response. Here’s an example of how to simulate network latency using WireMock:

wireMockRule.stubFor(get(urlEqualTo("/api/data"))
.willReturn(aResponse()
.withStatus(200)
.withFixedDelay(5000)));

This code configures WireMock to respond to requests to /api/data with a 200 status code and a fixed delay of 5000 milliseconds. This simulates network latency and delays the response by 5 seconds.

To test how your Spring Boot application handles network latency, you can use a tool like JMeter to simulate high network traffic and measure the response times of your application.

Simulate Connection Errors

Another type of network failure is a connection error, which can occur when there’s a network outage or connectivity issue. You can use WireMock to simulate connection errors by dropping connections or shutting down the server. Here’s an example of how to simulate a dropped connection using WireMock:

wireMockRule.stubFor(get(urlEqualTo("/api/data"))
.willReturn(aResponse()
.withFault(Fault.CONNECTION_RESET_BY_PEER)));

This code configures WireMock to respond to requests to /api/data with a connection reset error. This simulates a dropped connection and causes the client to receive a java.net.SocketException or java.io.IOException with the message "Connection reset by peer". Here’s an example of how to simulate a server shutdown using WireMock:

wireMockRule.shutdownServer();

This code shuts down the WireMock server, which simulates a network outage or server failure. This causes the client to receive a java.net.ConnectException or java.net.SocketTimeoutException with the message "Connection refused" or "Connection timed out".

To test how your Spring Boot application handles connection errors, you can use a tool like Postman to simulate requests and verify that your application responds gracefully to errors.

Simulate HTTP Errors

In addition to network failures, you may also want to test how your Spring Boot application handles HTTP errors, such as 400 Bad Request or 500 Internal Server Error. You can use WireMock to simulate HTTP errors by returning a non-200 status code in the response. Here’s an example of how to simulate a 500 Internal Server Error using WireMock:

wireMockRule.stubFor(get(urlEqualTo("/api/data"))
.willReturn(aResponse()
.withStatus(500)));

This code configures WireMock to respond to requests to /api/data with a 500 Internal Server Error status code. This simulates an internal server error and tests how your application handles unexpected errors.

Conclusion

WireMock is a powerful tool for simulating network failures and testing how your Spring Boot application handles unexpected network behavior. By simulating network latency, connection errors, and HTTP errors, you can identify and fix issues before they cause problems in production.

By using the examples above, you can get started with WireMock and start testing your application’s network resilience today!

Additionally, WireMock provides many other features for simulating APIs and testing your application, such as:

  • Request matching: You can configure WireMock to match incoming requests based on the URL, headers, body, and other criteria. This allows you to create complex scenarios and test different edge cases.
  • Response templating: You can use WireMock to dynamically generate responses based on the incoming request. This allows you to simulate dynamic APIs and test how your application handles different types of data.
  • Proxying: You can use WireMock to proxy requests to an external API and simulate different types of network behavior. This allows you to test how your application handles network latency, connection errors, and other types of failures.

Thanks for your attention! Happy Learning!

--

--