Spring Boot Integration Test with WireMock and Eureka fails with "No instances available"

4.3k views Asked by At

When writing an integration test for a Spring Boot application (Service A) that uses a RestTemplate (and Ribbon under the hood) and Eureka to resolve a Service B dependency, I get a "No instances available" exception when calling the Service A.

I try to mock the Service B away via WireMock, but I don't even get to the WireMock server. It seems like the RestTemplate tries to fetch the Service instance from Eureka, which doesn't run in my test. It is disabled via properties.

Service A calls Service B. Service discovery is done via RestTemplate, Ribbon and Eureka.

Does anyone have a working example that includes Spring, Eureka and WireMock?

4

There are 4 answers

0
Hannes On BEST ANSWER

I faced the same problem yesterday and for the sake of completeness here is my solution:

This is my "live" configuration under src/main/java/.../config:

//the regular configuration not active with test profile
@Configuration
@Profile("!test")
public class WebConfig {
    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        //you can use your regular rest template here.
        //This one adds a X-TRACE-ID header from the MDC to the call.
        return TraceableRestTemplate.create();
    }
}

I added this configuration to the test folder src/main/test/java/.../config:

//the test configuration
@Configuration
@Profile("test")
public class WebConfig {
    @Bean
    RestTemplate restTemplate() {
        return TraceableRestTemplate.create();
    }
}

In the test case, I activated profile test:

 //...
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServerCallTest {
   @Autowired
   private IBusiness biz;

   @Autowired
   private RestTemplate restTemplate;

   private ClientHttpRequestFactory originalClientHttpRequestFactory;

   @Before
   public void setUp() {
       originalClientHttpRequestFactory = restTemplate.getRequestFactory();
   }

   @After
   public void tearDown() {
      restTemplate.setRequestFactory(originalClientHttpRequestFactory);
   }

   @Test
   public void fetchAllEntries() throws BookListException {
      MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);

       mockServer                
            .andExpect(method(HttpMethod.GET))
            .andExpect(header("Accept", "application/json"))
            .andExpect(requestTo(endsWith("/list/entries/")))
            .andRespond(withSuccess("your-payload-here", MediaType.APPLICATION_JSON));

       MyData data = biz.getData();

       //do your asserts
   }
}
2
barbakini On

This is what I done in my project:

Somewhere in your project configuration:

@LoadBalanced
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    RestTemplate restTemplate = builder.build();
    restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    return restTemplate;
}

@Bean
public SomeRestClass someRestClass () {
    SomeRestClass someRestClass = new SomeRestClass (environment.getProperty("someservice.uri"), restTemplate(new RestTemplateBuilder()));
    return parameterRest;
}

And SomeRestClass:

public class SomeRestClass {

    private final RestTemplate restTemplate;

    private final String someServiceUri;

    public LocationRest(String someServiceUri, RestTemplate restTemplate) {
        this.someServiceUri= someServiceUri;
        this.restTemplate = restTemplate;
    }

    public String getSomeServiceUri() {
        return locationUri;
    }

    public SomeObject getSomeObjectViaRest() {
        //making rest service call
    }
}

And Test class for SomeRestClass:

@RunWith(SpringRunner.class)
@RestClientTest(SomeRestClass.class)
public class SomeRestClassTest {
    @Autowired
    private SomeRestClass someRestClass;

    @Autowired
    private MockRestServiceServer server;

    @Test
    public void getSomeObjectViaRestTest() throws JsonProcessingException {
        SomeResponseObject resObject = new SomeResponseObject();
        ObjectMapper objectMapper = new ObjectMapper();
        String responseString = objectMapper.writeValueAsString(resObject);

        server.expect(requestTo(locationRest.getSomeServiceUri() + "/some-end-point?someParam=someParam")).andExpect(method(HttpMethod.POST))
            .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).body(responseString));
        someRestClass.getSomeObjectViaRest();

    }
}

Note: I diasbled eureka client because otherwise you have to mock a eureka server. So I added eureka.client.enabled=false in test application.properties

0
Gonen I On

Hopefully this can help someone. I was getting the same error with Ribbon, but without Eureka.

What helped me was

1) Upgrading to the latest version of WireMock (2.21) in my case

2) Adding a wiremock rule stub for url "/" to answer Ribbon's ping

0
kanik On

If you are using Eureka just bypass in test/application.properties using eureka.client.enabled=false