When testing the REST API response created using Spring MVC's @RestController using MockMVC, use AssertJ to test the response JSON.
A mechanism for testing HTTP requests and responses to controllers without starting the server provided by Spring. Since the server is not started when the test is executed, there is an advantage that the test execution speed becomes faster. You can start the Spring application context by using @AutoConfigureMockMvc, which allows you to do DI as if you started the whole application.
Reference site: [Testing the Web Layer](Class ContentResultMatchers)
Testing with MockMVC + @ AutoConfigureMockMvc has the advantage of being able to test the entire application at high speed, I felt that the mechanism for checking the JSON returned from RestController was weak. There is also a method to compare the JSON of the response with the expected result, but you have to pass the JSON of the expected result as a string.
Reference site: Class ContentResultMatchers
For example, consider testing a RestContorller that returns a Book list like this:
Response class
Book.java
@Data
public class Book {
private int id;
private String name;
private String author;
}
Books.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Books {
private List<Book> books;
}
Controller class
BookController
@RestController
public class BookController {
//Service class that returns a Book list
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
@GetMapping("books")
public Books get() {
//Get the Book list with the service and return it
return bookService.getBooks();
}
}
In the ContentResultMatchers class there is a public ResultMatcher json (String jsonContent) method for checking JSON It is prepared. When using this json method, pass the JSON string of the expected result as an argument. Inside the json method, after converting the passed character string to a JSON object, it is compared with the response of the program under test.
BookControllerTest
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws Exception {
String expectedJson = "{books:[...]}"; //Create JSON of expected result as a string
mockMvc.perform(get("/books"))
.andExpect(status().isOk()) //Check the status code of the response
.andExpect(content().json(expectedJson)); //Check the JSON of the response
}
}
I wrote a test with the json method, but I thought as follows.
-If there are many JSON items, it is difficult to create a JSON character string with the expected result. In addition, typographical errors are likely to occur because IDE completion does not work for character strings. ↓ -It seems that there are fewer mistakes if you create a Java object and convert it to a character string with Jackson. ↓ -If creating a Java object, isn't it more efficient to convert the values returned from MockMVC into Java objects and compare them? Isn't it more convenient to use AssertJ for comparison between objects because it has more matchers?
MockMVC provides a method that can get the response body as a character string, so use this. Convert the obtained character string to an object with Jackson and compare it with the expected result using AssertJ. The code looks like this:
BookControllerTest
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws Exception {
String responseJsonString = mockMvc.perform(get("/books"))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString(); //Get the response body as a string
ObjectMapper objectMapper = new ObjectMapper();
//Convert to Java object in Jackson
Books responseJson = objectMapper.readValue(responseJsonString, Books.class);
//Create expected results with Java objects
Books expected = new Books(new ArrayList<>());
//Comparison
assertThat(responseJson).isEqualTo(expected);
}
}
Whether it is better to use the json method that is originally prepared depends on the content of JSON returned by the target API, and I think that there is also a preference. I hope it will be helpful to know that there is such a method.
Recommended Posts