[Test] 테스트 코드 작성 시 객체 비교

이명범·2022년 8월 21일
2

트러블슈팅

목록 보기
5/8
post-thumbnail

문제

서비스 레이어 단위 테스트를 작성하고 있었습니다!

작성한 테스트 코드는 다음과 같습니다.

코드 1 - isEqualTo()를 사용한 테스트 코드

@Test
@DisplayName("[회원 세부 정보 조회] 사용자는 자신의 정보를 조회할 수 있어야 한다.")
public void getMemberInfo() throws Exception {
    // given
    String id = "mungmnb777";
    Member expectedMember = MockEntityFactory.createMember();

    Mockito.when(memberRepository.findById(id)).thenReturn(Optional.of(expectedMember));

    // when
    MemberInfoResponseDto actual = memberService.getMemberInfo(id);

    MemberInfoResponseDto expected = new MemberInfoResponseDto();
    expected.convertFromEntity(expectedMember);

    // then
    assertThat(actual).isInstanceOf(MemberInfoResponseDto.class);
    assertThat(actual).isEqualTo(expected);
}

isEqualTo() 메서드는 값 비교 메서드이지만, 객체 비교를 할 때에는 참조 주소로 비교합니다.

그래서 Reflection을 이용해서 값 비교를 해봤어요.

코드 2 - Reflection을 활용한 테스트 코드

@Test
@DisplayName("[회원 세부 정보 조회] 사용자는 자신의 정보를 조회할 수 있어야 한다.")
public void getMemberInfo() throws Exception {
    // given
    String id = "mungmnb777";
    Member expectedMember = MockEntityFactory.createMember();

    Mockito.when(memberRepository.findById(id)).thenReturn(Optional.of(expectedMember));

    // when
    MemberInfoResponseDto actual = memberService.getMemberInfo(id);

    MemberInfoResponseDto expected = new MemberInfoResponseDto();
    expected.convertFromEntity(expectedMember);

    // then
    assertThat(actual).isInstanceOf(MemberInfoResponseDto.class);

    Method[] methods = MemberInfoResponseDto.class.getMethods();

    List<Method> filteredMethods = Arrays.stream(methods)
            .filter(m -> m.getName().contains("get"))
            .filter(m -> !m.getName().equals("getClass"))
            .collect(Collectors.toList());

    for (Method method : filteredMethods) {
        assertThat(method.invoke(actual)).isEqualTo(method.invoke(expected));
    }
}

그런데 이 때는 MemberInfoResponseDto 내부에서 참조하고 있는 AvatarResponseDto의 동일성 비교를 실패해서 테스트가 깨졌어요 😢

해결

DTO에 equals()hashcode()를 재정의하면 쉽게 해결할 수 있는 문제였지만, 다른 방법으로 해결을 해보고 싶었어요.

그래서 인터넷에서 assertj가 제공해주는 메서드가 있는지 한번 찾아봤어요.

결론적으로 usingRecursiveComparison()이라는 메서드를 이용하면 VO가 아닌 것들을 대상으로 내부 값 비교를 할 수 있다고 해요! 원리는 내부적으로 모든 값을 재귀적으로 돌면서 값을 비교한다고 하네요.

코드 3 - usingRecursiveComparision()을 사용한 테스트 코드

@Test
@DisplayName("[회원 세부 정보 조회] 사용자는 자신의 정보를 조회할 수 있어야 한다.")
public void getMemberInfo() throws Exception {
    // given
    String id = "mungmnb777";
    Member expectedMember = MockEntityFactory.createMember();

    Mockito.when(memberRepository.findById(id)).thenReturn(Optional.of(expectedMember));

    // when
    MemberInfoResponseDto actual = memberService.getMemberInfo(id);

    MemberInfoResponseDto expected = new MemberInfoResponseDto();
    expected.convertFromEntity(expectedMember);

    // then
    assertThat(actual).isInstanceOf(MemberInfoResponseDto.class);
    assertThat(actual).usingRecursiveComparison().isEqualTo(expected);
}

정상적으로 테스트를 통과했습니다!

profile
백엔드 개발자가 될거야

0개의 댓글