DummyMailSender에 대해 좀 더 생각해보자. DummyMailSender 클래스는 아무것도 하는 일이 없다. MailSender 인터페이스를 구현해놨을 뿐 메소드는 비어 있다. 하는 일이 없으면 가치도 없어야 할 텐데, 사실 이 클래스의 가치는 매우 크다. 이 클래스를 이용해 JavaMail로 메일을 직접 발송하는 클래스를 대치하지 않았다면 테스트는 매우 불편해지고 자주 실행하기 힘들었을 것이다.스프링의 XML 설정파일을 테스트용으로 따로 만든 이유는 무엇인가? 개발자 환경에서 손쉽게 이용할 수 있는 테스트용 DB를 사용하도록 만들기 위해서다. 그렇지 않고 운영 중인 DB 서버를 WAS의 풀링 서비스를 통해 이용하게 만들었다면 테스트를 자주 실행할 엄두도 못냈을지 모른다. 이처럼 테스트 환경에서 유용하게 사용하는 기법이 있다. 대부분 테스트할 대상이 의존하고 있는 오브젝트를 DI를 통해 바꿔치기하는 것이다.

서비스 추상화란 이렇게 원활한 테스트만을 위해서도 충분히 가치가 있다. 기술이나 환경이 바뀔 가능성이 있음에도, JavaMail처럼 확장이 불가능하게 설계해놓은 API를 사용해야 하는 경우라면 추상화 계층의 도입을 적극 고려해볼 필요가 있다. 특별히 외부의 리소스와 연동하는 대부분 작업은 추상화의 대상이 될 수 있다.

테스트 대상 오브젝트의 메소드가 돌려주는 결과뿐 아니라 테스트 오브젝트가 간접적으로 의존 오브젝트에 넘기는 값과 그 행위 자체에 대해서도 검증하고 싶다면 어떻게 해야 할까? 이 경우에는 단순하게 메소드의 리턴 값을 assertThat()으로 검증하는 것으로는 불가능하다. 이런 경우에는 테스트 대상의 간접적인 출력 결과를 검증하고, 테스트 대상 오브젝트와 의존 오브젝트 사이에서 일어나는 일을 검증할 수 있도로 특별히 설계된 목 오브젝트mock object를 사용해야 한다. 목 오브젝트는 스텁처럼 테스트 오브젝트가 정상적으로 실행되도록 도와주면서, 테스트 오브젝트와 자신의 사이에서 일어나는 커뮤니케이션 내용을 저장해뒀다가 테스트 결과를 검증하는 데 활용할 수 있게 해준다.

이렇게 테스트용으로 사용되는 특별한 오브젝트들이 있다. 대부분 테스트 대상인 오브젝트의 의존 오브젝트가 되는 것들이다. UserDao의 DataSource이거나, UserService의 MailSender 인터페이스를 구현한 것들이다. 이렇게 테스트 환경을 만들어주기 위해, 테스트 대상이 되는 오브젝트의 기능에만 충실하게 수행하면서 빠르게, 자주 테스트를 실행할 수 있도록 사용하는 이런 오브젝트를 통틀어서 테스트 대역test double이라고 부른다. 대표적인 테스트 대역은 테스트 스텁test stub이다. 테스트 스텁은 테스트 대상 오브젝트의 의존객체로서 존재하면서 테스트 동안에 코드가 정상적으로 수행할 수 있도록 돕는 것을 말한다. 일반적으로 테스트 스텁은 메소드를 통해 전달하는 파라미터와 달리, 테스트 코드 내부에서 간접적으로 사용된다. 따라서 DI 등을 통해 미리 의존 오브젝트를 테스트 스텁으로 변경해야 한다

목 오브젝트를 이용한 테스트라는 게, 작성하기는 간단하면서도 기능은 상당히 막강하다는 사실을 알 수 있을 것이다. 보통의 테스트 방법으로는 검증하기가 매우 까다로운 테스트 대상 오브젝트의 내부에서 일어나는 일이나 다른 오브젝트 사이에서 주고받는 정보까지 검증하는 일이 손쉽기 때문이다.

서비스 추상화는 테스트하기 어려운 JavaMail 같은 기술에도 적용할 수 있다. 테스트를 편리하게 작성하도록 도와주는 것만으로도 서비스 추상화는 가치가 있다.