반응형

테스트 더블과 실제 객체란


테스트 더블

  • 테스트에 필요한 협력 객체들을 가짜 객체로 대체하는 것
  • 내부 구현을 알아야하기 때문에 화이트 박스 테스트가 된다.

실체 객체

  • 테스트에 필요한 협력 객체들을 그대로 사용하는 것
  • 내부 구현을 알지 못하므로 블랙 박스 테스트가 된다.

classicist vs mockist


MemberService.java

// 내 회원 정보 조회
@Transactional(readOnly = true)
public Member findMemberOfMine(String email) {
    return memberRepository.findByEmail(email);
}

classicist

  • 대부분 실제 객체 사용을 선호하고, 실제 객체 사용이 어렵다면 테스트 더블을 사용한다.
  • 당연히 실제 객체만 사용하므로 상태 검증을하게 된다.
@Test
void 내_회원_정보_조회() {
    // given
    Member saveMember = memberRepository.save(createMember());

    // when
    Member member = memberService.findMemberOfMine(EMAIL);

    // then
    assertThat(member).isEqualTo(saveMember);
}

mockist

  • 대부분 가짜 객체를 사용하여 의존성을 제거한다.
    service 테스트 시 repository를 가짜 객체로 사용하는 것과 같다.
  • 가짜 객체를 사용하기 때문에 내부 구현을 커스텀한 응답으로 응답해야 한다.
  • 특정 행위가 실행되었는지 검증한다. (행위 검증)
@Test
void 내_회원_정보_조회() {
    // given
    when(memberRepository.findByEmail(anyString())).thenReturn(createMember());

    // when
    Member findMemberOfMine = memberService.findMemberOfMine(new LoginMember(createMember()));

    // then
    verify(memberRepository).findByEmail(anyString());
}

간단한 협력일 경우

먼저 나는 classicist이고, 위에 내 회원 정보 조회 같은 경우는 매우 간단하다.

  • classicist라면 실제 객체를 사용하여 상태 검증을 한다.
  • mockist라면 가짜 객체를 쓰고 행위 검증을 할 것이다.

절대 정답은 없지만, 나는 간단할 경우 굳이 가짜를 만들 필요가 없다고 생각한다.
가짜로 만들 경우 내부 구현이 findByEmail이 아닌 findByEmail2로 바뀌면
깨지는 테스트가 된다. 즉, 화이트 박스 테스트가 되기 때문이다.
DB에 의존할 필요가 없어서 가짜 객체를 사용한다는 말도 있는데 여기서는 DB에 접근하다라도
전혀 영향이 없다고 본다.
즉, 최대한 블랙 박스 테스트를 진행하며, 깨지기 쉽지 않은 테스트를 만들기 위함이다.

간단하지 않은 협력일 경우

  • classicist는 필요하다면, 가짜 객체를 사용한다. 실제 객체를 만들기 위해 복잡한 선 작업이 필요할 경우이다.
    예제 같은 경우는 단순히 save()만 해주기 때문에 간단한 협력이다.
  • mockist라면 당연히 가짜 객체를 사용한다.

상태 검증 vs 행위 검증


위에서 테스트 더블은 행위 검증 실제 객체는 상태 검증을 한다고 이야기했다.
절대적인건 아니며, 상황에 따라 섞어서 사용될 수도 있는 것이 프로그래밍이다.
그래도 정리하자면,

상태 검증

  • 복잡한 검증 데이터일 경우 상태 검증을 위해 검증될 데이터를 만들기 쉽지 않다.
  • 간단할 경우 위의 예제처럼 검증 데이터를 만들기 쉽다.

행위검증

  • 행위만 검증하기 때문에 검증 데이터를 만들 필요가 없다.
  • 쉬운만큼 검증될 데이터에 대한 고민이 적다.

하지만 극단적으로 정리한 것일 뿐 실제로는 섞어서 사용하는 일이 많을 것이다.

상태 검증을 어떻게 할 것인가?


먼저 내 회원 정보의 한 줄 소개라는 내용을 수정하는 메서드를 테스트한다고 하자.

MemberService.java

public void updateMemberOfMineIntroduction(String email, String introduction) {
    Member member = memberRepository.findByEmail(email);
    member.updateIntroduction(introduction);
}

영속성 컨텍스트에 의존할 것인가? DB에 의존할 것인가

@Test
void 내_회원_정보_수정_한줄_소개() {
    // given
    memberRepository.save(createMember());

    // when
    memberService.updateMemberOfMineIntroduction(EMAIL, "한줄 소개 변경");

    // then
    Member member = memberRepository.findByEmail(EMAIL);
    assertThat(member.getIntroduction()).isEqualTo("한줄 소개 변경");
}

findByEmail이 핵심이다.
먼저 findByEmail을 사용하든 안하든 성능상 전혀 문제가 없다.

영속성 컨텍스트에 의존

우리는 JPA를 사용하고 있다. 즉, save()Member를 생성하고
updateMemberOfMineIntroductionMember를 변경하게 된다.
그렇다면 이 시점에 Member는 영속성 컨텍스트에 변경이 적용된다.
즉, findByEmail이 필요없다. 어차피 findByEmail도 영속성 컨텍스트의 1차 캐시에서
가져오게 되기 때문에 save()가 응답한 값으로 상태 검증을 하면된다.

DB에 의존

JPA → JDBC로 변경된다고 가정을 해보자.
이 경우 findByEmail을 사용하지 않았다면, 이 테스트는 깨지게 된다.
영속성 컨텍스트가 없기 때문이다.
하지만 findByEmail을 사용했다면 DB로 조회하면 되기 때문에 깨지지 않는 테스트가 된다.

그래서 나는 DB에 의존할 수 있는 findByEmail을 선택했다.

나는 테스트를 만들 때 깨지기 쉽지 않은 테스트를 만들어야 한다고 생각한다.
테스트를 작성하는 이유는 테스트를 작성하기 위함이 아닌 구현을 잘 하기 위한 것이다.
그런데 테스트가 안깨져도 될 상황에 테스트가 깨진다면, 난감할 것이다.
결국 테스트 코드는 안짜게 되는 현상이 발생할 수도 있다.
그렇기 때문에 깨지기 쉽지 않는 테스트를 만드는 것이 좋다고 생각한다.

상태 검증은 하드 코딩으로

위의 한 줄 소개 검증을 하드 코딩으로 하지 않을 수 있다.

@Test
void 내_회원_정보_수정_한줄_소개() {
    // given
    Member saveMember = memberRepository.save(createMember());

    // when
    memberService.updateMemberOfMineIntroduction(EMAIL, "한줄 소개 변경");

    // then
    Member findMember = memberRepository.findByEmail(EMAIL);
    assertThat(saveMember.getIntroduction()).isEqualTo(findMember.getIntroduction());
}

나도 처음에는 이렇게 했었고, ATDD 과정을 들을 때도 크게 공감이 안되었는데
이번에 갑자기 공감이 되었다. 하드 코딩을 하지 않게 되면,(saveMember 사용)
updateMemberOfMineIntroduction()가 정상 동작할 것이라고 단정 짓고
상태 검증을 하는 것이다. 즉, updateMemberOfMineIntroduction()가 변경이 안되었을 경우
findByMember는 그대로 변경이 안된 값을 가져오게 될 것이고,
이걸 검증하는 것이다. 테스트는 당연히 통과하게 된다. 하지만 이게 updateMemberOfMineIntroduction()를
검증한 것인가? 아니다. 그렇기 때문에 내가 정말 검증할 값을 하드 코딩으로 테스트를 하는 것이
상태 검증이다.

반응형
복사했습니다!