테스트코드/개요

5. 목과 테스트의 취약성

feel2 2024. 3. 25. 20:10
반응형

목과 스텁 구분

테스트 대역은 목과 스텁으로 구분

  • 목(Mock)
    • 외부로 나가는 상호 작용을 모방하고 검사하는데 도움
    • SUT가 상태를 변경하기 위한 의존성을 호출
    • ex) 이메일 발송
  • 스텁(Stub)
    • 내부로 들어오는 상호작용을 모방하는데만 도움
    • SUT가 입력 데이터를 얻기 위한 의존성을 호출
    • ex) DB로부터 데이터 검색

테스트 대역은 크게 목과 스텁으로 나뉜다.

목과 스텁의 구분

 

도구로서의 목과 테스트 대역으로서의 목을 구분하자

  • 도구로서 목은 목 라이브러리(ex)Mockito)을 말함
  • 테스트 대역으로의 목은 위에서 말했던 외부로 나가는 상호 작용을 위한 목

 

목과 스텁을 함께 쓸수도 있다.

  • Mockito의 경우 둘 다 사용
@DataJpaTest
@ExtendWith(MockitoExtension.class)
@Slf4j
class StudyServiceTest {

    @Mock MemberService memberService;  // 선언적으로 Mock 만드는 방법

    @Mock StudyRepository studyRepository;

    @Test
    void createNewStudy() {

        // Given
        StudyService studyService = new StudyService(memberService, studyRepository);
        assertNotNull(studyService);

        // stub 세팅
        Member member = new Member();
        member.setId(1L);
        member.setEmail("asdf@email.com");

        Study study = new Study(10, "테스트");

        given(memberService.findById(1L)).willReturn(Optional.of(member));
        given(studyRepository.save(study)).willReturn(study);

        // When
        studyService.createNewStudy(1L, study);

        // Then
        assertNotNull(study.getOwnerId());
        assertEquals(member.getId(), study.getOwnerId());

        then(memberService).should(times(1)).notify(study);
        then(memberService).shouldHaveNoInteractions();
    }

}

given절에서 테스트 대역에 스텁을 세팅하고, 마지막 then절에서 목을 사용하여 SUT를 검증한다.

 

과잉명세

  • 최종 결과가 아닌 사항을 검증하는 것
    • ex) 스텁과의 상호작용을 검증하는 것
스텁과의 **상호 작용을 검증하는 것**은 일반적으로 안티 패턴이다.

 

CQS(Command Query Separation, 명령 조회 분리)

  • 명령(command)은 부작용을 일으키고 사이드 이펙트를 일으키고, 반환 값이 없는 메서드(return void)
    • 목(mock)에 해당
  • 조회(Query)는 사이드 이펙트가 없고, 값을 반환하는 것
    • 질문을 할때 답이 달라져서는 안된다.
    • 스텁(stub)에 해당

 

식별할 수 있는 동작과 구현 세부 사항

테스트는 ‘어떻게’가 아니라 ‘무엇’에 중점을 둬야 한다.

  • 모든 제품 코드는 2차원으로 분류 가능
    • 공개 API or 비공개 API
    • 식별할 수 있는 동작 or 구현 세부 사항
  • 잘 설계된 API는 식별할 수 있는 동작은 공개 API와 일치, 모든 구현 세부 사항은 비공개 API 뒤에 숨어 있음
  • 연산: 계산을 수행하거나 부작용을 초래하는 메서드
    • 하나의 기능에 하나의 연산이 있어야 한다. 하나의 기능에 여러 연산을 사용하면 응집도가 떨어짐
  • 상태: 시스템의 현재 상태
  • 잘 설계된 API → 단위 테스트 품질도 자동으로 올라감

 

목과 테스트 취약성 간의 관계

시스템 내 통신을 검증하고자 목을 사용하면 취약한 테스트로 이어짐

  • 통신과 해당 통신의 사이드 이펙트가 외부 환경에서 보일 때만 목을 사용해야 함
  • 시스템 내 통신을 검증하고자 하는 경우에는 되도록이면 목을 사용하지 말자.

 

참조

  • 단위테스트(블라디미르 크리코프)
반응형