JUnit테스트 실행 중 마주친 InvalidDefinitionException - 역직렬화 문제

sieun·2021년 7월 8일
1
post-thumbnail

개요

e-commerce 대용량 서버 프로젝트를 진행하던 중, Controller에서의 통합테스트가 어떠한 오류로 인해 실패하였습니다. 제가 겪었던 해당 오류의 해결과정을 기록해보려고 합니다.

code

해당 프로젝트는 다음 github에서 확인 가능합니다.
https://github.com/f-lab-edu/online-marketplace

UserControllerTest.class

@DisplayName("이메일이 중복된 회원이 아니라면 회원 가입에 성공한다.")
    @Test
    void signUp() throws Exception {
        // given
        final SignUpRequestDto dto = SignUpRequestDto.builder()
                .name(User1.NAME)
                .email(User1.EMAIL)
                .password(User1.PASSWORD)
                .phone(User1.PHONE)
                .build();

        // when
        final ResultActions actions = mvc.perform(post("/users/sign-up")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
                .andDo(print());

        // then
        actions
                .andExpect(status().isCreated())
                .andDo(print());
    }

발생한 오류

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.coupang.marketplace.controller.SignUpRequestDto]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.coupang.marketplace.controller.SignUpRequestDto` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 1, column: 2]

...(이하 생략)

해당 오류를 보면 SignUpRequestDto에 생성자가 없어 cannot deserialize from Object value 하여 InvalidDefinitionException이 발생한다고 합니다. 즉, 역직렬화 과정에서 문제가 있는 듯 싶은데.. 그렇다면 일단 SignUpRequestDto클래스를 보겠습니다.

SignUpRequestDto.class

@Getter
public class SignUpRequestDto {

    @NotBlank(message = "이름을 입력해주세요.")
    private String name;

    @NotBlank(message = "이메일을 입력해주세요.")
    @Email(message = "이메일 양식을 지켜주세요.")
    private String email;

    @NotBlank(message = "비밀번호를 입력해주세요.")
    private String password;

    @NotBlank(message = "휴대폰 번호를 입력해주세요.")
    @Pattern(regexp="[0-9]{10,11}", message = "10-11 자리의 전화번호를 입력해야 해요.")
    private String phone;

    @Builder
    public SignUpRequestDto(String name, String email, String password, String phone) {
        this.name = name;
        this.email = email;
        this.password = password;
        this.phone = phone;
    }

public SignUpRequestDto(String name, String email, String password, String phone){...}은 분명 생성자가 맞는데.. 왜 생성자가 없다고 하는 걸까요?

오류를 이어서 살펴보면,
노란 박스가 가리키는 곳은 다음과 같습니다.
여기서 writeValueAsString() 메소드는 Java오브젝트인 dto를 JSON으로 serializing(직렬화) 를 합니다. 즉 ObjectMapper로 dto를 JSON으로 변환합니다.
하지만 오류내용에서 보았듯이 deserialize(역직렬화) 하는 과정에서 문제가 발생한 듯 싶습니다.


😎여기서 멘토님이 힌트를 주셨습니다😎

생성자의 인자명은 컴파일시에 이름이 바뀌기 때문에 생성자를 찾지 못하는 경우가 있다

jackson은 역직렬화할때 생성자나 setter을 사용하는데 인자 이름이 바뀌어 버린 생성자를 찾지 못해 매핑시킬 수 없는 것입니다.

그럼 jackson이 생성자를 찾도록 해주려면 어떻게 해야할까요?

저는 @JsonProperty를 이용해보았습니다.

@Builder
    public SignUpRequestDto(@JsonProperty("name") String name, @JsonProperty("email") String email, @JsonProperty("password") String password, @JsonProperty("phone") String phone) {
        this.name = name;
        this.email = email;
        this.password = password;
        this.phone = phone;
    }

SignUpRequestDto생성자의 각 파라미터에 @JsonProperty를 사용하여 인자를 원하는 이름으로 JSON변환 시킬 수 있습니다.
@JsonProperty("name") String name인 경우에는 인자명 name 이 JSON으로 변환되어도 지정된 name 이라는 이름을 유지할 수 있습니다. 당연히 필요시에는 다른이름으로도 설정이 가능합니다.
그렇게 인자명을 지정시키면 jackson이 해당 인자들을 가진 생성자를 찾을 수 있어 매핑이 가능하게 됩니다.

결과

(테스트 성공😁)

후기

알고나서 오류 내용을 다시 보았을 때, 이제서야 보이는 해답..
(no delegate- or property-based Creator)
이미 정답을 알려주고 있었습니다..^^
하지만 이 오류해결 과정을 거치지 않았다면 이해를 할 수 없었겠죠😤



다른 방법

반면, 다음과 같은 방법이 아니더라도 build tool을 변경하여 오류를 해결할 수 있습니다.

스크린샷 2022-01-28 오전 12 33 32

preference - build, execution, deployment - build tools - gradle로 따라 들어가면 해당 화면을 볼 수 있는데, 빌드와 실행을 Gradle을 사용한다면 오류가 발생하지 않습니다.



📕 Reference

https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html
https://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.html

profile
열심히 공부중입니다😇

1개의 댓글

comment-user-thumbnail
2024년 7월 11일

생성자의 인자명은 컴파일시에 이름이 바뀌기 때문에 생성자를 찾지 못하는 경우가 있다

알고 있었던 개념 같은데 까먹었어요. 다시 리와인드 하게 해주셔서 감사합니다.

답글 달기