Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write integration tests with spring-cloud-netflix and feign

I use Spring-Cloud-Netflix for communication between micro services. Let's say I have two services, Foo and Bar, and Foo consumes one of Bar's REST endpoints. I use an interface annotated with @FeignClient:

@FeignClient public interface BarClient {   @RequestMapping(value = "/some/url", method = "POST")   void bazzle(@RequestBody BazzleRequest); } 

Then I have a service class SomeService in Foo, which calls the BarClient.

@Component public class SomeService {     @Autowired     BarClient barClient;      public String doSomething() {       try {         barClient.bazzle(new BazzleRequest(...));         return "so bazzle my eyes dazzle";       } catch(FeignException e) {         return "Not bazzle today!";       }      } } 

Now, to make sure the communication between services works, I want to build a test that fires a real HTTP request against a fake Bar server, using something like WireMock. The test should make sure that feign correctly decodes the service response and reports it to SomeService.

public class SomeServiceIntegrationTest {      @Autowired SomeService someService;      @Test     public void shouldSucceed() {       stubFor(get(urlEqualTo("/some/url"))         .willReturn(aResponse()             .withStatus(204);        String result = someService.doSomething();        assertThat(result, is("so bazzle my eyes dazzle"));     }      @Test     public void shouldFail() {       stubFor(get(urlEqualTo("/some/url"))         .willReturn(aResponse()             .withStatus(404);        String result = someService.doSomething();        assertThat(result, is("Not bazzle today!"));     } } 

How can I inject such a WireMock server into eureka, so that feign is able to find it and communicate with it? What kind of annotation magic do I need?

like image 850
Bastian Voigt Avatar asked Sep 13 '16 08:09

Bastian Voigt


People also ask

Does spring support integration testing?

The Spring Framework provides first-class support for integration testing in the spring-test module. The name of the actual JAR file might include the release version and might also be in the long org. springframework.


1 Answers

Here is an example of using WireMock to test SpringBoot configuration with Feign client and Hystrix fallback.

If you are using Eureka as a server discovery, you need to disable it by setting a property "eureka.client.enabled=false".

First, we need to enable the Feign/Hystrix configuration for our application:

@SpringBootApplication @EnableFeignClients @EnableCircuitBreaker public class Application {     public static void main(String[] args) {         SpringApplication.run(Application.class, args);     } }  @FeignClient(         name = "bookstore-server",         fallback = BookClientFallback.class,         qualifier = "bookClient" ) public interface BookClient {      @RequestMapping(method = RequestMethod.GET, path = "/book/{id}")     Book findById(@PathVariable("id") String id); }  @Component public class BookClientFallback implements BookClient {      @Override     public Book findById(String id) {         return Book.builder().id("fallback-id").title("default").isbn("default").build();     } } 

Please note that we are specifying a fallback class for the Feign client. Fallback class will be called every time Feign client call fails (e.g. connection timeout).

In order for tests to work, we need to configure the Ribbon loadbalancer (will be used internally by Feign client when sending http request):

@RunWith(SpringRunner.class) @SpringBootTest(properties = {         "feign.hystrix.enabled=true" }) @ContextConfiguration(classes = {BookClientTest.LocalRibbonClientConfiguration.class}) public class BookClientTest {      @Autowired     public BookClient bookClient;      @ClassRule     public static WireMockClassRule wiremock = new WireMockClassRule(             wireMockConfig().dynamicPort()));      @Before     public void setup() throws IOException {         stubFor(get(urlEqualTo("/book/12345"))                 .willReturn(aResponse()                         .withStatus(HttpStatus.OK.value())                         .withHeader("Content-Type", MediaType.APPLICATION_JSON)                         .withBody(StreamUtils.copyToString(getClass().getClassLoader().getResourceAsStream("fixtures/book.json"), Charset.defaultCharset()))));     }      @Test     public void testFindById() {         Book result = bookClient.findById("12345");          assertNotNull("should not be null", result);         assertThat(result.getId(), is("12345"));     }      @Test     public void testFindByIdFallback() {         stubFor(get(urlEqualTo("/book/12345"))                 .willReturn(aResponse().withFixedDelay(60000)));          Book result = bookClient.findById("12345");          assertNotNull("should not be null", result);         assertThat(result.getId(), is("fallback-id"));     }      @TestConfiguration     public static class LocalRibbonClientConfiguration {         @Bean         public ServerList<Server> ribbonServerList() {             return new StaticServerList<>(new Server("localhost", wiremock.port()));         }     } } 

Ribbon server list need to match the url (host and port) of our WireMock configuration.

like image 74
mladzo Avatar answered Sep 22 '22 10:09

mladzo