I have a REST resource that gets a RestTemplateBuilder
injected to build a RestTemplate
:
public MyClass(final RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
I would like to test that class. I need to mock the calls the RestTemplate
makes to another service:
request = restTemplate.getForEntity(uri, String.class);
I tried this in my IT:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIT {
@Autowired
private TestRestTemplate testRestTemplate;
@MockBean
private RestTemplateBuilder restTemplateBuilder;
@Mock
private RestTemplate restTemplate;
@Test
public void shouldntFail() throws IOException {
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
when(restTemplateBuilder.build()).thenReturn(restTemplate);
when(restTemplate.getForEntity(any(URI.class), any(Class.class))).thenReturn(responseEntity);
...
ResponseEntity<String> response = testRestTemplate.postForEntity("/endpoint", request, String.class);
...
}
}
When I run the test, I get the following exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.test.web.client.TestRestTemplate': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: RestTemplate must not be null
How do I do this correctly?
We can use Mockito to mock the RestTemplate altogether. With this approach, testing our service would be as simple as any other test involving mocking. In the above JUnit test class, we first asked Mockito to create a dummy RestTemplate instance using the @Mock annotation.
TestRestTemplate is not an extension of RestTemplate, but rather an alternative that simplifies integration testing and facilitates authentication during tests. It helps in customization of Apache HTTP client, but also it can be used as a wrapper of RestTemplate.
What is @RestClientTest? @RestClientTest is another one of Spring Boot's annotations used to test a specific slice of your application. As the name suggests, you can use it to test the REST clients inside your application. Applied to a test class it will disable the full-auto-configuration.
Your problem is the order of execution. The context is created containing your MockBean
before you have a chance to set it up in your @Test
. The solution is to provide a RestTemplateBuilder
that's already fully setup when it's inserted into the context. You can do that like this.
Add the following to your @SpringBootTest
annotation. Where TestApplication
is your Spring Boot application class.
classes = {TestApplication.class, MyIT.ContextConfiguration.class},
Modify your class members thus, deleting your restTemplate and restTemplateBuilder.
@Autowired
private TestRestTemplate testRestTemplate;
Add a static inner class to your MyIT
class:
@Configuration
static class ContextConfiguration {
@Bean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder rtb = mock(RestTemplateBuilder.class);
RestTemplate restTemplate = mock(RestTemplate.class);
when(rtb.build()).thenReturn(restTemplate);
return rtb;
}
}
In your tests, modify the RestTemplate
mock to do whatever you want:
@Test
public void someTest() {
when(testRestTemplate.getRestTemplate().getForEntity(...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With