Spring Boot WireMock - Testing HTTP clients

In this section, we'll develop a Spring Boot application that connects with a jsonplaceholder client with the help of RestClient and at the end we will perform integration testing with WireMock and JUnit 5.

jsonplaceholder

JSONPlaceholder is a free online REST API that we can use whenever we need some fake data.


RestClient

RestClient is a synchronous client to perform HTTP requests. It is a higher-order API since it performs HTTP requests by using an HTTP client library like the JDK HttpClient, Apache HttpComponents, and others. More Info - click here


WireMock

WireMock is a popular open-source library for API mock testing. More Info - click here

Advantages:

     > No load on the remote server.

     > Full control over the response.

     > Works offline.          


JUnit 5

For writing and running unit tests and integration tests for any Java application, the JUnit framework is a great option. It is already included in the spring-boot-starter-test module of Spring Boot 3.


The Spring Framework provides the following Rest clients for making calls to REST endpoints:

  • RestClient (Supporting from Spring 6.1 onwards)
  • WebClient
  • HTTP Interface
  • RestTemplate

In this example we are using RestClient client for making call to REST endpointWe will implement a service that will fetch data from the JSONPlaceholder API. 


Complete example

First, open the Spring initializr https://start.spring.io

Then, Provide the Group and Artifact name. We have provided Group name com.knf.dev.demo and Artifact spring-boot-wiremock-example. Here I selected the Maven project - language Java 17 - Spring Boot 3.3.1, Spring Web, and Lombok.

Then, click on the Generate button. When we click on the Generate button, it starts packing the project in a .zip(spring-boot-wiremock-example) file and downloads the project. Then, Extract the Zip file. 

Then, import the project on your favourite IDE. 


Final Project Directory


Then, add WireMock.

   <dependency>
   <groupId>com.maciejwalkowiak.spring</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<version>2.1.2</version>
<scope>test</scope>
</dependency>

Complete pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-boot-wiremock-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-wiremock-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.maciejwalkowiak.spring</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<version>2.1.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

application.properties

spring.application.name=spring-boot-wiremock-example

post.base.url = https://jsonplaceholder.typicode.com/posts

Base URL of post API(real) is declared inside application.properties. 


Create RestClient

package com.knf.dev.demo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;

@Configuration
public class RestClientConfig {

@Value("${post.base.url}")
String baseUrl;

@Bean
RestClient restClient() {
return RestClient.create(baseUrl);
}
}

 

Create Post Model

package com.knf.dev.demo.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Post {

public int id;
public String title;
public String body;
public int userId;
}


Create PostClient

package com.knf.dev.demo.client;

import com.knf.dev.demo.model.Post;

public interface PostClient {

Post findById(int id);

}


Create PostClientImpl.java

package com.knf.dev.demo.client;

import com.knf.dev.demo.model.Post;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

@Component
@AllArgsConstructor
public class PostClientImpl implements PostClient {

private final RestClient restClient;

@Override
public Post findById(int id) {
Post post = restClient.get()
.uri("/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(req, res) -> {
// TODO
}
)
.onStatus(HttpStatusCode::is5xxServerError,
(req, res) -> {
//TODO
})
.body(Post.class);

return post;
}
}


Application.java

package com.knf.dev.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

}


Basic Stub - Create get_post_with_id_1.json

We can configure stubs using JSON configuration file or java code. Here I am using JSON file.

By default, each WireMockServer is configured to load mapping files from a classpath directory wiremock/{server-name}/mappings.

Path: src/test/resources/wiremock/post-client/mappings/get_post_with_id_1.json

{
"request": {
"method": "GET",
"url": "/1"
},
"response": {
"status": 200,
"jsonBody": {
"userId": 1,
"id": 1,
"title": "Spring Boot Wire Mock Example",
"body": "Testing HTTP clients in Spring Boot app with wiremock"
},
"headers": {
"Content-Type": "application/json"
}
}
}


Create PostClientTest.java

package com.knf.dev.demo;

import com.knf.dev.demo.client.PostClient;
import com.knf.dev.demo.model.Post;
import com.maciejwalkowiak.wiremock.spring.ConfigureWireMock;
import com.maciejwalkowiak.wiremock.spring.EnableWireMock;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@EnableWireMock({
@ConfigureWireMock(name = "post-client", property = "post.base.url")
})
public class PostClientTest {

@Autowired
private PostClient postClient;

@Test
void shouldGetPost_whenFetchingWithValidId() {
Post post = postClient.findById(1);
assertThat(post.getId()).isEqualTo(1);
assertThat(post.getTitle()).isEqualTo("Spring Boot Wire Mock Example");
assertThat(post.getBody()).isEqualTo("Testing HTTP clients in " +
"Spring Boot app with wiremock");
assertThat(post.getUserId()).isEqualTo(1);
}

}

  • @EnableWireMock adds test context customizer and enables WireMockSpringExtension
  • @ConfigureWireMock creates a WireMockServer and passes the WireMockServer#baseUrl to a Spring environment property with a name given by property.


Run the Test

Or you can run the test using following command:

mvn  test -Dtest=PostClientTest

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete