Reputation: 21
I need to write a unit test for a rest controller endpoint that calls a service which calls RestTemplate exchange()
@RestController
public class MyController {
@Autowired
Myservice myservice;
@GetMapping("todo")
public ResponseEntity<String> getTodo() {
String todoJson = myservice.getTodo();
return ResponseEntity.ok(todoJson);
}
}
Here's my service class
@Service
public class Myservice {
public String getTodo() {
ResponseEntity<Todo> response = restTemplate.exchange("https://jsonplaceholder.typicode.com/todos/1", HttpMethod.GET, null, Todo.class);
Todo todo = response.getBody();
return objectMapper.writeValueAsString(todo);
}
}
And test case
@ExtendWith(SpringExtension.class)
class MyControllerTestJunit5 {
private MockMvc mockMvc;
@Mock
private RestTemplate restTemplate;
@InjectMocks
MyController myController;
@Mock
Myservice myservice;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
@Test
public void mockResttemplate() throws Exception {
Todo todosample = new Todo(5,6,"myfield", true);
ResponseEntity<Todo> responseEntity = ResponseEntity.ok(todosample);
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.eq(Todo.class)))
.thenReturn(responseEntity);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/todo"))
.andExpect(status().isOk())
.andReturn();
String myresult = result.getResponse().getContentAsString();
System.out.println("response: " + myresult);
}
printing the response at the end shows that the response is empty. How can I get this to work?
Upvotes: 0
Views: 492
Reputation: 3296
I assume that you expect to get the JSON representation of todosample
object. The problem in the test is that you are mocking Myservice
by annotating it with @Mock
. That mocked service is used by MyController
because it's annotated with @InjectMocks
. As a result, when you make a call to the /todo
endpoint the controller calls myservice.getTodo()
which returns null
as the Myservice
mock is not configured to return anything else. After that, the null
value gets propagated to the ResponseEntity
body which materializes to the empty OK response in your test.
I believe a better approach for this kind of test will be to just mock Myservice
and configure it properly. The first part is done already as I mentioned. The configuration is easy. You should just replace restTemplate.exchange
call configuration with the myservice.getTodo()
one. Something like this should work:
@Test
public void mockResttemplate() throws Exception {
String todosampleJson = "{}"; // just empty json
when(myservice.getTodo()).thenReturn(todosampleJson);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/todo"))
.andExpect(status().isOk())
.andReturn();
String myresult = result.getResponse().getContentAsString();
System.out.println("response: " + myresult); // will print "{}" now
}
And then you can have a separate test for Myservice
where you have to mock restTemplate.exchange
call.
Of course technically, you can still get away with mocking the restTemplate
in your MyController
test but then you have to make sure you instantiate the real Myservice
instead of the mocked version of it. However, in this case you would expose the implementation details of Myservice
to the MyController
class, which kinda contradicts the MVC pattern.
Upvotes: 1