I want to mock a RestTemplate
in Spring Boot, where I'm making a REST call within a method. To test the controller of the microservice I'm creating,
I want to test methods inside the controller of my micro service.
For example:
@GetMapping(value = "/getMasterDataView", produces = { MediaType.APPLICATION_JSON_VALUE })
@CrossOrigin(origins = { "http://192.1**********" }, maxAge = 3000)
public ResponseEntity<MasterDataViewDTO> getMasterDataView() throws IOException {
final String uri = "http://localhost:8089/*********";
RestTemplate restTemplate = new RestTemplate();
MasterDataViewDTO masterDataViewDTO = restTemplate.getForObject(uri, MasterDataViewDTO.class);
return new ResponseEntity<>(masterDataViewDTO, HttpStatus.OK);
}
how to I test this using mocking?
This is what I have so far:
@Test
public void testgetMasterDataView() throws IOException {
MasterDataViewDTO masterDataViewDTO= mock(MasterDataViewDTO.class);
//String uri = "http://localhost:8089/*********";
Mockito.when(restTemplate.getForObject(Mockito.anyString(),ArgumentMatchers.any(Class.class))).thenReturn(masterDataViewDTO);
assertEquals("OK",inquiryController.getMasterDataView().getStatusCode());
}
I am getting an error when I'm running the mock, the method getMasterDataView()
is getting called and the REST call within it is also getting called and is throwing an error. How can I write my test so that the REST endpoint is not called? If it's possible, I'd like to do this with Mockito.
Before you start writing a test, you should change your code a bit. First of all, it would be a lot easier if you extracted that RestTemplate
, and created a separate bean for it which you would inject within your controller.
To do that, add something like this within a @Configuration
class or within your main class:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
Additionally, you have to remove the new RestTemplate()
from your controller, and autowire it in stead, for example:
@Autowired
private RestTemplate restTemplate;
Now that you've done that, it's going to be a lot easier to inject a mock RestTemplate
within your tests.
For your testing, you have two options:
- Either mock
RestTemplate
and all the methods you are trying to access, using a mocking framework (eg. Mockito)
- Or you can use
MockRestServiceServer
, which allows you to write tests that verify if the URLs are properly called, the request matches, and so on.
Testing with Mockito
To mock your RestTemplate
with Mockito, you have to make sure that you add the following annotation to your tests:
@RunWith(MockitoJUnitRunner.class)
After that, you can do this:
@InjectMocks
private MyController controller;
@Mock
private RestTemplate restTemplate;
And now you can adjust your tests like this:
@Test
public void testgetMasterDataView() throws IOException {
MasterDataViewDTO dto = new MasterDataViewDTO();
when(restTemplate.getForObject("http://localhost:8089/*********", MasterDataViewDTO.class)).thenReturn(dto);
ResponseEntity<MasterDataViewDTO> response = controller.getMasterDataView();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo(dto);
}
You could mock the DTO as you did within your test, but you don't have to, and I don't think there's any benefit from doing so. What you do have to mock is the restTemplate.getForObject(..)
call.
Testing with MockRestServiceServer
Another approach is to use MockRestServiceServer
. To do that, you have to use the following annotations for your test:
@RunWith(SpringRunner.class)
@RestClientTest
And then you'll have to autowire your controller and MockRestServiceServer
, for example:
@Autowired
private MyController controller;
@Autowired
private MockRestServiceServer server;
And now you can write tests like this:
@Test
public void testgetMasterDataView() throws IOException {
server
.expect(once(), requestTo("http://localhost:8089/*********"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(new ClassPathResource("my-mocked-result.json"), MediaType.APPLICATION_JSON));
ResponseEntity<MasterDataViewDTO> response = controller.getMasterDataView();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
// TODO: Write assertions to see if the DTO matches the JSON structure
}
In addition to testing that your actual REST call matches, this also allows you to test if your JSON-to-DTO works as well.
You can achieve this by using @RestClientTest
and MockRestServiceServer
. An example provided in their documentation:
@RunWith(SpringRunner.class)
@RestClientTest(RemoteVehicleDetailsService.class)
public class ExampleRestClientTest {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
public void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
throws Exception {
this.server.expect(requestTo("/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}