단위 테스트 스타일
단위 테스트 스타일이란?
출력 기반, 상태 기반, 통신 기반이라는 세 가지 스타일로 테스트를 분류할 수 있다.
하나의 테스트에서 하나 또는 두 세가지 스타일을 모두 함께 사용할 수 있다.
단위 테스트의 두 분파는 선호하는 스타일이 다르다. 고전파 → 통신 기반 스타일 < 상태 기반 스타일 런던파 → 상태 기반 스타일 < 통신 기반 스타일
출력 기반 스타일
output-based testing 테스트 대상에 입력을 넣고 출력된 반환 값을 검증하는 방식
테스트 코드가 간단하고 명확하므로 테스트 품질이 가장 좋은 스타일
전역 상태나 내부 상태를 변경하지 않아 외부 의존성을 다루지 않기 때문에 반환 값만 검증하면 된다.
순수 함수처럼 사이드 이펙트가 없는 코드에만 적용할 수 있다.
기반 코드를 함수형 아키텍처를 지향하게끔 구성하면 출력 기반 스타일의 테스트 코드를 작성할 수 있게 된다.
출력 기반 단위테스트는 함수형이라고도 한다.
사이드 이펙트가 없는 코드를 선호하는 함수형 프로그래밍에 뿌리를 두고 있다.
상태 기반 스타일
state-based testing
작업이 완료 된 후 시스템의 최종 상태를 검증하는 방식
여기서 말하는 상태는
협력자
또는 외부 의존성의 상태 등을 의미한다.
협력자란 SUT(System Under Test, 테스트 대상이 되는 코드)와 상호작용 하는 모듈, 객체, 함수를 등을 가리킨다.
통신 기반 스타일
communication-based testing
목을 사용해 테스트 대상 시스템과 협력자 간의 통신을 검증하는 방식
테스트 대상의 협력자를 목으로 대체하고, 협력자를 올바르게 호출하는지를 검증한다.
단위 테스트 스타일 비교
좋은 단위 테스트의 4대요소를 스타일 별로 비교해보자.
결론부터 말하자면 출력 기반 스타일이 가장 우수하다.
기반 코드를 순수함수로 구성하면, 상태 기반이나 통신 기반의 테스트 스타일을 출력 기반의 테스트로 변경할 수 있게 된다.
좋은 단위 테스트의 4대요소에는 회귀 방지, 리팩터링 내성, 빠른 피드백, 유지 보수성이 있다.
회귀 방지
테스트가 얼마나 버그(회귀)의 존재를 잘 나타내는지에 대한 척도
회귀 방지 지표는 테스트 중에 실행되는 코드량에 따라 달라지는데, 실행되는 코드량은 스타일에 따라 구분되는 것이 아닌 테스트 코드를 어느정도로 테스트할지 상황에 따라 달리지므로 연관이 없다.
빠른 피드백
테스트가 얼마나 빨리 실행되는지에 대한 척도
모든 테스트 스타일은 단위 테스트 기준이므로 테스트 실행속도는 별 차이 없다.
리팩터링 내성
리팩터링 중에 발생하는 거짓 양성 수에 대한 척도
거짓 양성은 코드의 내부 구현사항에 결합된 테스트의 결과이다.
출력 기반 스타일은 테스트 대상 메서드에만 결합되므로 리팩터링 내성에 가장 우수하다.
상태 기반 스타일은 테스트 대상 외에도 다른 협력자의 상태와 함께 작동하므로 구현 세부사항과 결합할 가능성이 커진다.
통신 기반 테스트는 테스트 대역에 상호작용을 확인하는데 이런 테스트는 대부분 깨지기 쉬운 테스트이므로 가장 취약하다.
유지 보수성
테스트가 얼마나 이해하기 어려운지, 테스트가 얼마나 실행하기 어려운지에 대한 척도
출력 기반 스타일은 대부분 짧고 간결하므로 유지 보수성이 용이하다.
상태 기반 스타일은 출력 검증보다 더 많은 데이터를 필요로하고 확인해야하므로 상대적으로 복잡하다.
반복되는 코드를 재사용하기 위한 헬퍼 메서드나, fixtures를 활용하면 완화시킬 수 있다.
통신 기반 스타일은 테스트 대역과 상호작용을 검증해야하므로 테스트 코드가 커지고 복잡해진다.
출력 기반 테스트로 리팩토링 with 함수형 아키텍처
상태 기반 테스트와 통신 기반 테스트를 출력 기반 테스트 스타일로 리팩토링 해보자.
코드 내의 외부 의존성을 목(mock)으로 대체할수 있게 주입받는 형식으로 관심사를 분리 시킨다.
함수형 아키텍처는 사이드 이펙트를 비즈니스 연산 끝으로 몰아서 비즈니스 로직과 사이드 이펙트를 분리한다.
비즈니스 로직과 사이드 이펙트 분리하기
코드를 두 가지 유형으로 나누면, 비즈니스 로직과 부수 효과를 분리하는 것이 더 쉬워진다.
결정을 내리는 코드(함수형 코어) → 사이드 이펙트가 필요 없기에 순수 함수로 구현한다.
해당 결정에 따라 작용하는 코드(가변 쉘) → 순수 함수에 의해 이뤄진 모든 결정을 사이드 이펙트를 적용해 결정을 해석한다.
순수 함수는 테스트 코드가 짧고 간결하며 이해하고 유지보수하기 쉬우므로 테스트하기 쉬운 코드가 된다.
동작 순서
가변 쉘에서 모든 입력 데이터를 함수형 코어에 공급
코어에서 결정(값)을 생성하고 반환
쉘에서 반환받은 결정을 사이드 이펙트로 변환
목표는 함수형 코어 부분을 출력 기반 테스트로 다루는 것
Last updated