@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)에 입력을 넣고 생성되는 출력을 점검하는 방식이다. 출력 기반 단위 테스트 스타일은 함수형 이라고도 한다. 출력 기반 테스트의 기반 코드는 전역 상태나 내부 상태를 변경할 리 없으므로, 프로세스 외부 의존성을 다루지 않는다.
@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);
}
상태 기반 스타일은 작업이 완료된 후 시스템 상태를 확인하는 것이다.
@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");
}
항상 다른 것보다 출력 기반 테스트를 선호하라.
출력 기반 테스트를 적용할 수 있는 메서드 유형은 수학적 함수뿐이다. 이는 유지 보수성이 뛰어나고 거짓 양성 빈도가 낮다.
수학에서의 함수는 첫 번째 집합의 각 요소에 대해 두 번째 집합에서 정확히 하나의 요소를 찾는 두 집합 사이의 관계다.
반면에 숨은 입출력은 코드를 테스트하기 힘들게 한다(가독성도 떨어짐). 숨은 입출력의 유형은 다음과 같다.
메서드가 수학적 함수인지 판별하는 가장 좋은 방법은 프로그램의 동작을 변경하지 않고 해당 메서드에 대한 호출을 반환 값으로 대체할 수 있는지 확인해보는 것이다.
함수형 프로그래밍의 목표는 사이드 이펙트를 완전히 제거하는 것이 아니라 비즈니스 로직을 처리하는 코드와 사이드 이펙트를 일으키는 코드를 분리하는 것이다. 사이드 이펙트를 비즈니스 연산 끝으로 몰아서 비즈니스 로직을 사이드 이펙트와 분리한다.
다음 두 가지 코드 유형을 구분해서 비즈니스 로직과 사이드 이펙트를 분리할 수 있다.