Spring Boot — Unit test a Feign Client
I am dedicating this article to my dear friend and colleague Li Gang who helped me devise this mechanism to test a Feign Client (using name parameter and no target URL) without having the need to spin up the real target service.
To get hold of the code referred to in this article please visit the repository @ https://github.com/sumantrana/FeignClientExample.git
Feign Client
Feign client provides a declarative mechanism for the clients to consume rest based APIs. Spring Cloud integrates Feign with Ribbon and Eureka to provide service discovery as well as inherent client side load balancing.
For more details on Feign client and how spring integrates it with Eureka and Ribbon please refer to the following link https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html.
Prerequisites:
- Spring Boot
- Open Feign
- Netflix Ribbon
Create a Feign Client
Create a simple feign client calling a remote method hello
on a remote service identified by name test
. We want to test this Feign client without the creation of target service.
If we define a url as a parameter of the
FeignClient
annotation, then we can simply usewiremock
or equivalent library to spin up a mock endpoint (corresponding to that url) and test the client against it. But in this case we want to be able to use name instead of the url. For an example of the URL based testing, as well as for the name based testing (described below) please refer to the gitlab URL mentioned above.
@FeignClient(name = "test")
public interface FeignAPI {
@RequestMapping(value = "hello")
String hello();
}
Testing
Create a Ribbon Configuration
Create a ribbon configuration to return a list of servers. In our case since we are running tests on local machine, we will return instances of localhost
listening on the random port
assigned by SpringBootTest
web environment.
@Configuration
static class RibbonConfig {
@LocalServerPort
int port;
@Bean
public ServerList<Server> serverList() {
return new StaticServerList<>(new Server("127.0.0.1", port), new Server("127.0.0.1", port));
}
}
Create a Feign Configuration
This configuration will use the Ribbon configuration defined previously to fetch the list of servers where the mock service would be listening. It also creates a rest endpoint for the feign client to hit. This rest endpoint should have the same semantics as the business rest end point (that we are testing using feign client).
EnableFeignClients
annotation ensures that spring boot creates a bean for FeignAPI
and injects it into the context.
@EnableFeignClients(clients = FeignAPI.class)
@RestController
@Configuration
@EnableAutoConfiguration
@RibbonClient(name = "test", configuration = FeignAPITest.RibbonConfig.class)
static class FeignConfig {
@RequestMapping(value = "hello")
public String testFeign() {
return "success";
}
}
Create the test
Finally, create the test. This has to be a spring boot test. This test will enable the Feign configuration defined above and bring up the environment on a random port. This random port is used by the Ribbon configuration to create mock servers for hosting the mock service.
@SpringBootTest(classes = FeignAPITest.FeignConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FeignAPITest {
@Autowired
FeignAPI feignAPI;
@Test
public void testFeign() {
assertThat(this.feignAPI.hello()).isEqualTo("success");
}
This way the Feign client is tested in isolation environment calling the server based on the name parameter.