컨트롤러 단위 테스트를 완벽하게 짰다고 생각했는데 자꾸 HttpMessageNotReadableException가 발생했다.
분명 저번에 같은 문제를 해결했었는데.. 낚시로 물고기를 많이잡다보니 뇌가 물고기화 되가는 것 같다. 이번에 기록하고 기억해야겠다.
문제가 발생했던 상황을 최대한 간단하게 코드로 작성하면 아래와 같다.
@Test
void changeOrderStatus() throws Exception{
Order order = createOrderWithOrderStatus(OrderStatus.MEAL);
String content = new ObjectMapper().writeValueAsString(order);
mockMvc.perform(put("/api/orders/{orderId}/order-status", "1")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
...
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(value = EnumType.STRING)
private OrderStatus orderStatus;
private LocalDateTime orderedTime;
protected Order() {
}
...
문제가 없어보인다. Order 객체를 생성한 후 Json형식으로 바꾸고 content 메서드에 담아서 보냈다. 그런데 계속 HttpMessageNotReadableException가 발생했다.
원인은 바로 LocalDateTime에 있었다.
ObjectMapper 객체로 LocalDateTime을 json으로 변환하면
orderedTime":{"month":"NOVEMBER","year":2020,"dayOfMonth":18,"hour":23,"minute":16,"monthValue":11,"nano":991000000,"second":56,"dayOfWeek":"WEDNESDAY","dayOfYear":323,"chronology":{"id":"ISO","calendarType":"iso8601"}
이렇게 변환된다. 이 값을 @RequestBody Order order
로 받으려하니깐 안받아지고 HttpMessageNotReadableException가 터진 것 같다.
정리해보면 LocalDateTime이 직렬화/역직렬화가 안되는 것이 아니라 @RequestBody로 받아오는 객체에서 원하는 형태로 변환되지 않기 때문에 HttpMessageNotReadableException가 발생하는 것이다.
해결 방법은 간단하다. 나의 경우엔 테스트에서만 ObjectMapper를 사용해서 간단하게 LocalDateTime을 json으로 변환하는 것을 원했기때문에 아래와 같은 방법을 사용했다.
@Test
void changeOrderStatus() throws Exception{
Order order = createOrderWithOrderStatus(OrderStatus.MEAL);
String content = new ObjectMapper()
.registerModule(new JavaTimeModule())
.writeValueAsString(order);
mockMvc.perform(put("/api/orders/{orderId}/order-status", "1")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
...
핵심은 @RequestBody로 받아오는 객체에서도 읽을 수 있게 형식을 지정해주는 것이다. 위의 경우엔 .registerModule(new JavaTimeModule())로 형식을 지정해줬기에 아까와는 다르게 아래와 같은 형태로 json이 변환된다.
orderedTime":[2020,11,18,23,37,6,99000000]
이렇게 변환된 json은 @RequestBody로 충분히 받아올 수 있다.
ObjectMapper를 사용하지않고 아래와 같은 방식들로 json 데이터를 만들어서 요청을 보내는 경우엔
// 이렇게 json형식을 만들어 get 요청
String url = "/params?orderedTime=2020-11-18T11:00:00";
// 이렇게 json형식을 만들어 content에 담아 요청
.content("{\"orderedTime\":\"2020-11-18T11:00:00\"}"));
Entity의 LocalDateTime변수에 직접 @DataTimeFormat 이나 @JsonFormat 어노테이션을 사용해서 쉽게 해결할 수 있다.
관련해서는 킹졸두님의 글에 설명이 너무 잘되어있다. 필요한 상황에 보고 찾아서 적용하면 좋겠다.
'Spring' 카테고리의 다른 글
STS Maven java.lang.ClassNotFoundException (0) | 2021.03.17 |
---|---|
Objects.isNull과 Validation (0) | 2020.11.20 |
DDD에서는 왜 간접 참조를 더 권장할까? (0) | 2020.10.10 |
WebMvcConfigurer addResourceHandlers 경로 설정 (0) | 2020.09.12 |
@Mock vs @MockBean (0) | 2020.08.14 |