Reputation: 751
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?
Upvotes: 5
Views: 4671
Reputation: 451
If you are using Eureka just bypass in test/application.properties using eureka.client.enabled=false
Upvotes: 0
Reputation: 6107
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
Upvotes: 0
Reputation: 2073
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
}
}
Upvotes: 3
Reputation: 3134
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
Upvotes: 0