Issue
Problem
I'm trying to write a test for a Spring controller but the test itself doesn't seem to return anything. What's odd is that if I manually test this with Postman I get the expected results.
Expecting
What I'm expecting
{
"title": "My Third Todo"
}
What I'm getting (empty response?)
Relevant Code
TodoService
@Service
public class TodoService {
private TodoRepository todoRepository;
@Autowired
public TodoService(TodoRepository todoRepository) {
this.todoRepository = todoRepository;
}
public List<Todo> findAll() {
return todoRepository.findAll();
}
public Todo findById(Long todoId) {
return todoRepository.findById(todoId).orElse(null);
}
public Todo save(Todo todo) {
return todoRepository.save(todo);
}
public void deleteById(Long todoId) {
todoRepository.deleteById(todoId);
}
}
TodoController
@RestController
@RequestMapping("/api/todos")
public class TodoController {
private TodoService todoService;
@Autowired
public TodoController(TodoService todoService) {
this.todoService = todoService;
}
@PostMapping
public Todo save(@RequestBody Todo todo) {
return todoService.save(todo);
}
}
TodoControllerTest
@RunWith(SpringRunner.class)
@WebMvcTest(TodoController.class)
public class TodoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TodoService todoService;
private List<Todo> todos;
public TodoControllerTest() {
Todo firstTodo = new Todo();
firstTodo.setId(1L);
firstTodo.setTitle("My First Todo");
Todo secondTodo = new Todo();
secondTodo.setId(2L);
secondTodo.setTitle("My Second Todo");
todos = new ArrayList<>();
todos.add(firstTodo);
todos.add(secondTodo);
}
@Test
public void save() throws Exception {
Todo thirdTodo = new Todo();
thirdTodo.setId(3L);
thirdTodo.setTitle("My Third Todo");
when(todoService.save(thirdTodo)).thenReturn(thirdTodo);
String mock = mockMvc.perform(post("/api/todos")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"title\": \"My Third Todo\"}"))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
System.out.println("\n\n\n");
System.out.println("Mock: " + mock);
System.out.println("\n\n\n");
}
}
Solution
It appears that when sending up JSON in a POST request you need to have a 1:1 mapping of the object that your JSON represents. In my above example in my @Test
I did not do this.
Example Class
public class MyObject {
private Long id;
private String title;
// Constructor, getters, and setters
}
With the example class my MyObject
, when you want to pass this to RequestBuilder.post(...).content(...)
you apparently need to have that 1:1 mapping like so
RequestBuilder.post(...)
content("{\"id\":1,\"title\":\"My Third Todo\"}\")
Another suggestion is to use ObjectMapper
to convert an object to JSON.
Todo todo = new Todo(1L, "My Third Todo");
ObjectMapper mapper = new ObjectMapper();
String todoJson = mapper.writeValueAsString(todo);
mockMvc.perform(RequestBuilder.post("/api/todos")
.contentType(MediaType.APPLICATION_JSON)
.content(todoJson))
// do whatever else you need
Answered By - Brandon Benefield