지난 시간에 이어 TDD 책을 읽고 공부한 내용을 정리해보려고 한다.
기능은 상황에 따라 결과가 달라진다. 테스트 코드는 기능을 실행하고 그 결과를 확인하므로 상황, 실행, 결과 확인(given - when - then)의 3가지 요소로 테스트를 구성할 수 있다. 어떤 상황이 주어지고, 그 상황에서 기능을 실행하고, 실행한 결과를 확인하는 3가지가 테스트 코드의 기본 골격을 이룬다.
그런데, 파일 시스템, 데이터베이스 접근, 외부 HTTP 서버와 통신 등 테스트가 외부 요인에 의존하는 경우, 테스트 결과의 일관성을 확보하기 어렵다. 외부 상태에 따라 테스트의 성공 여부가 바뀌지 않으려면 테스트 실행 전에 외부를 원하는 상태로 만들거나 테스트 실행 후에 외부 상태를 원래대로 되돌려 놓아야 한다.
하지만, 외부 상태를 테스트에 맞게 구성하고 제어하는 것은 어렵다. 이러한 경우 대역을 사용하여 외부 요인이나 결과를 대체할 수 있다.
대역이란 특정 객체의 동작을 흉내내는 객체이다.
제어하기 힘든 외부 상황이 존재하면 다음과 같은 방법으로 의존을 도출하고 이를 대역으로 대신한다.
모의 객체를 이용하면 대역 클래스를 만들지 않아도 되니깐 처음에는 편할 수 있다. 하지만 결과 값을 확인하는 수단으로 모의 객체를 사용하기 시작하면 결과 검증 코드가 길어지고 복잡해진다. 특히 하나의 테스트를 위해 여러 모의 객체를 사용하기 시작하면 결과 검증 코드의 복잡도는 배로 증가한다. 게다가 모의 객체는 기본적으로 메서드 호출 여부를 검증하는 수단이기 때문에 테스트 대상과 모의 객체 간의 상호 작용이 조금만 바뀌어도 테스트가 깨지기 쉽다.
이런 이유로 모의 객체의 메서드 호출 여부를 결과 검증 수단으로 사용하는 것은 주의해야 한다. 특히 DAO나 리포지토리와 같이 저장소에 대한 대역은 모의 객체를 사용하는 것보다 메모리를 이용한 가짜 구현을 사용하는 것이 테스트 코드 관리에 유리하다.