5. 목과 테스트 취약성

weekbelt·2023년 2월 6일
0

5.1 목과 스텁 구분

5.1.1 테스트 대역 유형

  • 목은 외부로 나가는 상호 작용을 모방하고 검사하는 데 도움이 된다. 이러한 상호 작용은 SUT가 상태를 변경하기 위한 의존성을 호출하는 것에 해당한다.
  • 스텁은 내부로 들어오는 상호 작용을 모방하는 데 도움이 된다. 이러한 상호 작용은 SUT가 입력 데이터를 얻기 위한 의존성을 호출하는 것에 해당한다

5.1.2 도구로서의 목과 테스트 대역으로서의 목

class ControllerTest {

    @Mock
    private IEmailGateway emailGateway;		// mock 생성

    @Mock
    private IDatabase database;				// mock 생성

    @Test
    public void sending_a_greetings_email() {
        // given
        Controller sut = new Controller(emailGateway, database);

        // when
        sut.greetUser("user@gamil.com");

        // then
        then(emailGateway)
            .should(times(1))
            .sendGreetingEmail("user@email.com");	// 테스트 대역으로 하는 SUT의 호출을 검사
    }

    @Test
    public void creating_a_report() {
        // given
        given(database.getNumberOfUsers()).willReturn(10);		// stub 생성
        Controller sut = new Controller(emailGateway, database);

        // when
        Report report = sut.createReport();

        // then
        assertThat(10).isEqualTo(report.getNumberOfUsers());
    }

}

5.1.3 스텁으로 상호 작용을 검증하지 말라

  • SUT에서 스텁으로의 호출은 SUT가 생성하는 최종 결과가 아니라 최종 결과를 산출하기 위한 수단일 뿐이다.

스텁과의 상호 작용을 검증하는 것은 취약한 테스트를 야기하는 일반적인 안티 패턴이다.

5.1.4 목과 스텁 함께 쓰기

    @Test
    public void purchase_fails_when_not_enough_inventory() {
        // given
        Store storeMock = mock(Store.class);
        given(storeMock.hasEnoughInventory(Product.SHAMPOO, 5)).willReturn(false);		// 준비된 응답을 설정
        Customer sut = new Customer();

        // when
        boolean success = sut.purchase(storeMock, Product.SHAMPOO, 5);

        // then
        assertThat(success).isFalse();
        then(storeMock).should(times(0)).removeInventory(Product.SHAMPOO, 5);	// SUT에서 수행한 호출을 검사
    }

5.1.5 목과 스텁은 명령과 조회에 어떻게 관려돼 있는가?

  • CQS(Command Query Separation)원칙에 따르면 모든 메서드는 명령이거나 조회여야 하며, 이 둘을 혼용해서는 안 된다.
  • 메서드가 사이드 이펙트를 일으키면 해당 메서드의 반환타입이 void인지 확인하라. 그리고 메서드가 값을 반환하면 사이드 이펙트가 없어야 한다.
  • 가능한한 CQS원칙을 따라라.

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

  • 테스트 코드와 코드의 구현 세부사항의 강결합을 피하는 방법은 코드가 생성하는 최종 결과(식별할 수 있는 동작)를 검증하고 구현 세부 사항과 테스트를 가능한 한 떨어뜨리는 것뿐이다.
  • 구현 세부 사항을 숨기면 클라이언트의 시야에서 클래스 내부를 가릴 수 있기 때문에 내부를 손상시킬 위험이 적다.
  • 데이터와 연산을 결합하면 해당 연산이 클래스의 불변성을 위반하지 않도록 할 수 있다.
  • 모든 구현 세부 사항을 비공개로 하면 테스트가 식별할 수 있는 동작을 검증하는 것 외에는 다른 선택지가 없으며, 이로 인해 리팩터링 내성도 자동으로 좋아진다.

참고

profile
백엔드 개발자 입니다

0개의 댓글