@Test
@WithMockUser(roles="USER")
@Transactional // 프록시 객체에 실제 데이터를 불러올 수 있게 영속성 컨텍스트에서 관리
public void comment_등록() throws Exception {
// given
String title = "title";
String content = "content";
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.author("author")
.build();
postsRepository.save(requestDto.toEntity());
/* 게시글 등록 */
String comment = "comment";
Posts posts = postsRepository.findAll().get(0);
CommentSaveRequestDto saveRequestDto = CommentSaveRequestDto.builder()
.comment(comment)
.posts(posts)
.build();
/* requestDto 생성 */
Long id = posts.getId();
String url = "http://localhost:"+ port + "/api/posts/" + id + "/comments";
//when
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(saveRequestDto)))
.andExpect(status().isOk())
.andDo(print());
}
댓글 기능을 만들고, 이것을 post에 대한 테스트를 만들었습니다.
댓글 기능을 추가한 뒤, 댓글 등록에 대한 테스트를 작성했습니다.
@Transactional
처음에는 해당 애노테이션 없이 테스트를 실행했는데, 이런 에러가 나왔습니다
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: springboot.domain.posts.Posts.comments, could not initialize proxy - no Session (through reference chain: springboot.web.dto.comment.CommentSaveRequestDto["posts"]->springboot.domain.posts.Posts["comments"])
에러가 발생한 곳은
.content(objectMapper.writeValueAsString(saveRequestDto)))
dto를 통해 post를 실행하는 라인입니다.
무슨 에러인가 구글링해본 결과
라고 하는데..
https://stackoverflow.com/questions/16752799/could-not-initialize-proxy-no-session
첫번째 답변에 따르면,
라고 합니다. 답변을 거의 그대로 옮겼더니 장황해졌네요.. 저의 경우는 처리 사이클안에서 자원을 내려놓지 않게(세션이 열려있게) 해야할 것 같습니다.
lazy로딩으로 서로를 연결해주기 위해서는 Posts를 영속성 컨텍스트(트랜잭션 범위와 동일) 안에서 관리해줄 필요가 있어보입니다.
따라서 @Transaction 어노테이션을 통하여 메서드가 시작하고 끝날때까지의 일련의과정을 쪼갤수 없는 하나의 연산으로 만들어주었습니다.
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(new ObjectMapper().writeValueAsString(saveRequestDto)))
.andExpect(status().isOk())
.andDo(print())
이렇게 ObjectMapper 객체를 새로 생성하여 테스트를 진행하면
java.lang.AssertionError: Status
Expected :200
Actual :400
이렇게 에러가 발생합니다.
정확한 에러가 알고 싶어서 프린트 문 안에 mvc구문을 넣고 다시 돌려보았는데요,
DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
at [Source: (PushbackInputStream); line: 1, column: 45] (through reference chain: springboot.web.dto.comment.CommentSaveRequestDto["posts"]->springboot.domain.posts.Posts["createdDate"])]
createdDate를 JSON으로 파싱하는 과정에서 에러가 발생한듯 합니다.
구글링을 해서 어렵지 않게 저의 경우에 딱 들어맞는 솔루션을 찾을 수 있었습니다.
https://www.inflearn.com/questions/30590
정리하자면,
스프링은 내부적으로 LocalDateTime을 처리할 때 ISO8601 형태를 사용하는데, 객체형태로 값이 들어오기에 스프링 프레임워크가 이를 해석할 수 없다.
따라서 스프링이 제공하는 ISO8601형태가 설정된 ObjectMapper를 @Autowired ObjectMapper objectMapper; 이렇게 주입받아서 사용하면 된다
라고 합니다.
참고
https://cantcoding.tistory.com/78
https://stackoverflow.com/questions/16752799/could-not-initialize-proxy-no-session
https://www.inflearn.com/questions/30590
https://stackoverflow.com/questions/50362883/what-is-the-advantage-of-declaring-objectmapper-as-a-bean