Unit Testing FeignClient using RestController and RibbonClient

Learn to write a JUnit test for FeignClient with SpringBootTest using RestController to mock the remote API and RibbonClient to feed FeignClient with local-host endpoint URL of the RestController

Andy Lian
codeburst

--

The API layer is one of the most crucial components in a microservice-based application. It is the channel that connects one service to another, drives business processes, and provides the services which give value to users. API is an agreed contract between two services. Before any implementation test can begin, it is important to make sure that your written component behaves as the agreed contract.

In this article, we will learn how to write JUnit test for FeignClient using SpringBootTest. We will be setting up our unit test context with a RestController to mock the remote API and a RibbonClient to feed our FeignClient with a local endpoint URL to our RestController.

Prerequisites

You can use the Spring Initializr website to generate a Maven project with Spring Boot 2.x dependency. Add Web, OpenFeign, and Ribbon to your project as well.

Spring Initializr — Add Dependencies

If you are starting off with an empty Maven project, import Spring Cloud Dependencies POM so your project could inherit all the artifacts versions in Spring Cloud family.

<!-- spring-cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR6</version>
<scope>import</scope>
<type>pom</type>
</dependency>

Next, add Spring Boot Starter Web, Spring Cloud Starter OpenFeign and Spring Cloud Starter Netflix Ribbon module into your project’s dependency.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

Spring uses OpenFeign under the hood to build the client component that does the rest-based service call and uses Netflix Ribbon to provide client-side load balancing to the provisioned client component.

Besides adding the dependencies to use FeignClient and RibbonClient, we need Spring Boot Starter Test module dependency as well which imports both Spring Boot Test module as well JUnit, Hamcrest, and a number of other useful libraries for unit testing.

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

This step is not necessary if you are generating your project file with Spring Initializr since it's always included as part of the dependency.

Alternatively, you can download the Maven project from GitHub.

Create a REST Client Interface

In this article, I will briefly go through creating a REST client using FeignClient and focus more on the unit testing part.

Let’s consider here that we have a Spring Boot application with Client Discovery feature enabled and a FeignClient that has a GetMapping request method and a PostMapping request method.

@FeignClient(
name = "messagingRestClient",
path = "/messaging")
public interface MessagingRestClient {
@GetMapping(
params = {"name"})
Message getMessage(
@RequestParam("name") final String name);
@PostMapping(
params = {"name"})
Message setMessage(
@RequestParam("name") final String name,
@RequestBody final Message message);

}

Our MessagingRestClient is given an arbitrary name “messagingRestClient” and request path “/messaging” with FeignClient annotation. The GET request method is expected to accept a String name parameter and return a Message object. The POST request method is expected to accept a Spring name parameter and a Message object body and return a Message object.

Finally, the Message class is just a simple Data Transfer Object (DTO) with a String text property.

public class Message {  private String text;  public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}

Create a Unit Test Class

Now let us move on to the main dish, creating a unit test for our FeignClient. Create a MessagingRestClientBootTests class with @SpringBootTest annotation using a random port web environment.

@SpringBootTest(
webEnvironment = WebEnvironment.RANDOM_PORT)
class MessagingRestClientBootTests {
}

@SpringBootTest will create an about complete application context for MessagingRestClientBootTests containing all the objects we need to run our unit test.

Create a Fake RestController to Mock Remote API

Create a FakeMessagingRestService static class that will be used to mock the remote Messaging REST API to receive requests from FeignClient.

@RestController
@RequestMapping(
path = "/messaging")
static class FakeMessagingRestService {
}

FakeMessagingRestService will be exposed to path “/messaging” matching exactly to MessagingRestClient request path.

The GET request method in FakeMessagingRestService is returning a String response rather than returning the expected Message object. This is part of the integration test to assert the REST API returned JSON String can be converted to Message object and returned to the provisioned FeignClient.

@GetMapping(
params = {"name"},
produces = "application/json")
public String getMessage(
@RequestParam("name") final String name) {

assertThat(name)
.isEqualTo("Foo");

return "{\"text\":\"Hello, Foo\"}";
}

For Spring to know that the REST API is returning a JSON String, we need to explicitly set this GET request method will produceapplication/json" content type in GetMapping annotation.

produces = "application/json"

Likewise, the POST request in FakeMessagingRestService is accepting a String name and String request body rather than the expected Message object. This is also part of the integration test to assert the message parameter is correctly converted to JSON String when calling the REST API.

@PostMapping(
params = {"name"},
produces = "application/json")
public String setMessage(
@RequestParam("name") final String name,
@RequestBody final String message)
throws Exception {

assertThat(name).isEqualTo("Foo");
JSONAssert
.assertEquals(message,
"{ \"text\":\"Hello\" }", false);

return "{\"text\":\"Hi, Foo\"}";
}

Like the GET request, the POST request is returning a String response instead of a message object. Again, we need to explicitly set this POST request method to produce “application/json" content type in PostMapping annotation.

produces = "application/json"

Create a Fake Ribbon Configuration

Create a FakeRibbonConfiguration static class to return a ServerList bean.

@Configuration(
proxyBeanMethods = false)
static class FakeRibbonConfiguration {
@LocalServerPort int port; @Bean
public ServerList<Server> serverList() {
return new StaticServerList<>(
new Server("localhost", port));
}
}

The ServerList bean contains only a single server endpoint referring to the localhost and random port web environment created using SpringBootTest.

Create a Fake Feign Configuration

Create a FakeFeignConfiguration static class to enable auto-configuration and FeignClient creation for MessagingRestClient.

@Configuration(
proxyBeanMethods = false)
@EnableFeignClients(
clients = MessagingRestClient.class)
@EnableAutoConfiguration
@RibbonClient(
name = "messagingRestClient",
configuration =
MessagingRestClientBootTests.FakeRibbonConfiguration.class)
static class FakeFeignConfiguration {}

FakeFeignConfiguration also uses the FakeRibbonConfiguration to create a client-side load balancer for MessagingRestClient when calling to REST API.

Finally, we need to include FakeMessagingRestService and FakeFeignConfiguration classes as part of the component to be loaded when creating our test application context.

@SpringBootTest(
classes = {
MessagingRestClientBootTests.FakeFeignConfiguration.class,
MessagingRestClientBootTests.FakeMessagingRestService.class
},
webEnvironment = WebEnvironment.RANDOM_PORT)

Create a GET Request Test Method

After all this preparation work, let’s create a test method for the GET request. First, we need to auto-wire the Spring created MessagingRestClient bean.

@Autowired MessagingRestClient client;

Next, create a getMessage() test method.

@Test
public void getMessage() {
final Message response =
client.getMessage("Foo");
assertThat(response.getText())
.isEqualTo("Hello, Foo");
}

Call client.getMessage() method supplying name parameter with “Foo” and assert that the received message object contains “Hello, Foo” text.

Create a POST Request Test Method

Similarly, create a setMessage() test method.

@Test
public void setMessage() {
final Message message = new Message();
message.setText("Hello");
final Message response =
client.setMessage("Foo", message);
assertThat(response.getText())
.isEqualTo("Hi, Foo");
}

Call client.setMessage method supplying the parameter with “Foo” and the message object containing “Hello” text. Assert that the received message object contains “Hi, Foo” text.

Summary

In this article, we have learned how to write a unit test for FeignClient using SpringBootTest, as well as how to create a fake RestController to receive requests, and to create a fake RibbonClient to supply FeignClient with target REST API endpoint.

All source code is available on GitHub. I hope this article has been helpful. Thanks for reading!

--

--

Full-stack software developer with a wide variety of coding niches skills used in building various aspects of IT solutions