[Spring] JUnit & Mockito 테스팅 중 HttpMessageNotWritableException 에러

kai6666·2022년 7월 15일
0

TIL. Spring

목록 보기
7/11
post-thumbnail

에러 코드 (테스트 실패)

@Test
    void patchMemberTest() throws Exception  {
        MemberDto.Patch patch = new MemberDto.Patch(1L, "패치테스트", "010-1234-5678",null);
        Member member = mapper.memberPatchToMember(patch);
        member.setStamp(new Stamp());
        
        ...(이하 생략)...

통과 코드 (테스트 통과)

@Test
    void patchMemberTest() throws Exception {
        MemberDto.Patch patch = new MemberDto.Patch(1L, "패치테스트", "010-1234-5678", Member.MemberStatus.MEMBER_ACTIVE);
        Member member = mapper.memberPatchToMember(patch);
        member.setStamp(new Stamp());
        
        ...(이하 생략)...


😵‍💫 에러 메시지

  • 에러 메시지를 보면 NullPointerException 때문에 JSON을 작성할 수 없다고 한다. 구체적으로 member.dto.MemberDto$response["memberStatus"], 그러니까 memberStatus에서 Null이 난다고 알려준다.
  • NullPointerException면 왜 HttpMessageNotWritableException이 날아왔을까? 다채로운 에러의 세상에서 처음 만난 녀석이니 바로 공식 문서로 슝슝.

❓ HttpMessageNotWritableException

  • HttpMessageNotWritableException는 런타임 에러, 그중 HttpMessageConversionException의 한 종류인 것을 확인 할 수 있다.
  • 설명에 따르면 HttpMessageConverter가 쓰기(write)에 실패했을 때 날아오는 에러다.

👉 에러의 발생 경위를 순차적으로 정리해보자면

  • memberStatus가 비어있음
  • NullPointerException이 남
  • = 명령한대로 JSON을 쓸 수 없음
  • = HttpMessageConverter.write() 실패
  • 이것을 HttpMessageNotWritableException라고 부름.

널포인터 대신 이 에러메시지가 뜨는 이유는, 코드가 명령하는 것이 쓰기 동작이기 때문에 그에 대응하는 에러가 HttpMessageNotWritableException이고, 이 에러가 발생한 이유가 NullPointerException이기 때문에 뒤따라서 알려준 것 같다.


해결 노력 - (1) @Nullable

@Nullable
private Member.MemberStatus memberStatus;

MemberDto 클래스의 Patch 부분에 회원상태에 @Nullable 애너테이션을 붙여줬다. null이어도 되게 설정하면 NullPointerException이 나지 않고, 따라서 컨버터 에러도 나지 않겠지 하는 생각으로 했는데 동일한 에러가 났다.

@Nullable 사용법이 잘못 된 것인가 하고 공식문서를 봤는데,

null을 리턴해도 되는 메서드나, null이어도 되는 변수(필드, 지역 변수, 매개 변수)의 경우에 붙이는 것이라하니 의미는 맞다. 그러나 annotation helps you detect, 그러니까 이것을 변수에 붙여준다고 설정을 바꿔주는 게 아니라 그냥 인지하게 해주는 정보제공용(?) 애너테이션인 것이다... 그러니 똑같이 NPE가 날 수밖에.


해결 노력 - (2) 설정값, 생성자 확인

애초에 내가 왜 memberStatus는 null이어도 된다고 생각하게 됐지?하는 의문이 생겼다. 아마도 테이블을 만들고 DB를 배울 때 @Column(nullable=true)로 설정해두었기 때문 같은데...

@Enumerated(value = EnumType.STRING)
@Column(length = 20, nullable = false)
private MemberStatus memberStatus = MemberStatus.MEMBER_ACTIVE;

nullable=false ...언제나처럼 정답은 코드에 있었다^^
민망해서 글을 싹 지우고 싶은 충동이 들었으나 우선은 끝까지 적어보겠습니다(..)

@Getter @AllArgsConstructor
    public static class Patch {
    ...(이하 생략)...

그리고 dto 클래스에 나는 @AllArgsConstructor를 붙여놓았다.
클래스의 모든 필드에 대한 인자를 요구하는 애너테이션이다. 때문에 Patch에 속한 memberStatus도 null이 아닌 인자를 넘겨주어야 했던 것이다.

자주 썼기 때문에 아는 애너테이션이라 여겼는데, 이런 기초적인 부분을 간과해서 동굴 탐험을 한 느낌이다. 애초에 내가 애매모호하게 파악하고 넘어갔기 때문이라 생각한다. 오늘 기록한 내용은 두 번 다시 까먹지 않으리라🥹


참고 자료

profile
성장 아카이브

0개의 댓글