본문 바로가기
개발관련 서적 정리/Unit Testing

6. 단위 테스트 스타일

by Backchus 2023. 2. 8.

6.1 단위 테스트의 세 가지 스타일

  • 출력 기반 테스트(output-based testing)

  • 상태 기반 테스트(state-based testing)

  • 통신 기반 테스트(communication-based testing)

    6.1.1 출력 기반 테스트 정의

      @Test
      public void discount_of_two_products() {
          // given
          Product product1 = new Product("Hand Wash");
          Product product2 = new Product("Shampoo");
          PrinceEngine sut = new PrinceEngine();
    
          // when
          double discount = sut.calculateDiscount(List.of(product1, product2));
    
          // then
          assertThat(discount).isEqualTo(0.02);
      }

    테스트 대상 시스템(SUT)에 입력을 넣고 생성되는 출력을 점검하는 방식이다. 출력 기반 단위 테스트 스타일은 함수형 이라고도 한다. 출력 기반 테스트의 기반 코드는 전역 상태나 내부 상태를 변경할 리 없으므로, 프로세스 외부 의존성을 다루지 않는다.

    6.1.2 상태 기반 스타일 정의

      @Test
      public void adding_a_print_to_an_order() {
          // given
          Product product = new Product("Hand Wash");
          Order sut = new Order();
    
          // when
          sut.addProduct(product);
    
          // then
          assertThat(sut.getProducts().size()).isEqualTo(1);
          assertThat(sut.getProducts().get(0)).isEqualTo(product);
      }

    상태 기반 스타일은 작업이 완료된 후 시스템 상태를 확인하는 것이다.

    6.1.3 통신 기반 스타일 정의

      @Test
      public void sending_a_greetings() {
          // given
          IEmailGateway emailGatewayMock = mock(IEmailGateway.class);
          Controller sut = new Controller(emailGatewayMock);
    
          // when
          sut.greetUser("user@email.com");
    
          // then
          then(emailGatewayMock).should(times(1)).sendGreetingEmail("user@email.com");
      }

    6.1.4 결론

    항상 다른 것보다 출력 기반 테스트를 선호하라.

6.3 함수형 아키텍처 이해

출력 기반 테스트를 적용할 수 있는 메서드 유형은 수학적 함수뿐이다. 이는 유지 보수성이 뛰어나고 거짓 양성 빈도가 낮다.

수학에서의 함수는 첫 번째 집합의 각 요소에 대해 두 번째 집합에서 정확히 하나의 요소를 찾는 두 집합 사이의 관계다.

반면에 숨은 입출력은 코드를 테스트하기 힘들게 한다(가독성도 떨어짐). 숨은 입출력의 유형은 다음과 같다.

  • 사이드 이펙트: 사이드 이펙트는 메서드 시그니처에 표시되지 않는 출력이다. 따라서 숨어있다. 연산은 클래스 인스턴스의 상태를 변경하고 디스크의 파일을 업데이트하는 등 사이드 이펙트를 발생시킨다.
  • 예외: 메서드가 예외를 던지면, 프로그램 흐름에 메서드 시그니처에 설정된 계약을 우회하는 경로를 만든다. 호출된 예외는 호출 스택의 어느 곳에서도 발생할 수 있으므로, 메서드 시그니처가 전달하지 않는 출력을 추가한다.
  • 내외부 상태에 대한 참조: LocalDateTime.now()와 같이 정적 속성을 사용해 현재 날짜와 시간을 가져오는 메서드가 있을 수 있다. 데이터베이스에서 데이터를 질의할 수 있고, 비공개 변경 가능 필드를 참조할 수도 있다. 이 모두 메서드 시그니처에 없는 실행 흐름에 대한 입력이며, 따라서 숨어있다.

메서드가 수학적 함수인지 판별하는 가장 좋은 방법은 프로그램의 동작을 변경하지 않고 해당 메서드에 대한 호출을 반환 값으로 대체할 수 있는지 확인해보는 것이다.

6.3.1 함수형 아키텍처란?

함수형 프로그래밍의 목표는 사이드 이펙트를 완전히 제거하는 것이 아니라 비즈니스 로직을 처리하는 코드와 사이드 이펙트를 일으키는 코드를 분리하는 것이다. 사이드 이펙트를 비즈니스 연산 끝으로 몰아서 비즈니스 로직을 사이드 이펙트와 분리한다.

다음 두 가지 코드 유형을 구분해서 비즈니스 로직과 사이드 이펙트를 분리할 수 있다.

  • 결정을 내리는 코드: 이 코드는 사이드 이펙트가 필요 없기 때문에 수학적 함수를 사용해 작성할 수 있다.
  • 해당 결정에 따라 작용하는 코드: 이 코드는 수학적 함수에 의해 이뤄진 모든 결정을 데이터베이스의 변경이나 메시지 버스로 전송된 메시지와 같이 가시적인 부분으로 변환한다.

    참고

  • http://www.yes24.com/Product/Goods/104084175