[Clean Architecture] 28장. 테스트 경계
|
Introduction
- 테스트는 시스템의 일부이며, 아키텍처에도 관여함
시스템 컴포넌트인 테스트
- 테스트에 관련한 상당한 혼동
- 테스트는 시스템의 일부인가? 아니면 별개인가?
- 어떤 종류의 테스트가 있는가?
- 단위 테스트와 통합 테스트는 서로 다른가?
- 인수 테스트, 기능 테스트, Cucumber 테스트, TDD 테스트, BDD 테스트, 컴포넌트 테스트 등은 어떻지?
- 아키텍처 관점에서는 모든 테스트가 동일함
- 테스트는 태생적으로 의존성 규칙을 따름
- 테스트는 세부적이며 구체적인 것으로, 의존성은 항상 테스트 대상이 되는 코드를 향함
- 실제로 테스트는 아키텍처에서 가장 바깥쪽 원으로 생각할 수 있음
- 시스템 내부의 어떤 것도 테스트에는 의존하지 않으며, 테스트는 시스템의 컴포넌트를 향해, 항상 원의 안쪽으로 의존함
- 테스트는 독립적으로 배포 가능함
- 사실 대다수의 경우 테스트는 테스트 시스템에만 배포하며, 상용 시스템에는 배포하지 않음
- 심지어 배포 독립성이 달리 필요하지 않은 시스템에서도 테스트는 독립적으로 배포될 것
- 테스트는 시스템 컴포넌트 중에서 가장 고립되어 있음
- 테스트가 시스템 운영에 꼭 필요치는 않음
- 어떤 사용자도 테스트에 의존하지 않음
- 테스트의 역할은 운영이 아니라 개발을 지원하는 데 있음
- 그렇다고 해서 테스트가 시스템의 컴포넌트가 아니란 뜻은 아님
- 사실 많은 면에서 테스트는 다른 모든 시스템 컴포넌트가 반드시 지켜야 하는 모델을 표현해줌
테스트를 고려한 설계
- 개발자는 종종 테스트가 시스템의 설계 범위 밖에 있다고 여기지만, 이 관점은 치명적
- 테스트가 시스템 설계와 잘 통합되지 않으면, 테스트는 깨지기 쉬워지고, 시스템은 뻣뻣해져서 변경하기가 어려워짐
- 문제는 결합
- 시스템에 강하게 결합되 테스트라면, 시스템이 변경될 때 함께 변경되어야만 함
- 시스템 컴포넌트에서 생긴 아주 사소한 변경도, 이와 결합된 수많은 테스트를 망가뜨릴 수 있음
- 깨지기 쉬운 테스트 문제가 수천 개정도 되면 상황은 더 심각해질 수 있음
- 깨지기 쉬운 테스트는 시스템을 뻣뻣하게 만든다는 부작용을 낳을 때가 많음
- 시스템에 가한 간단한 변경이 대량의 테스트 실패로 이어진다는 사실을 알게 되면, 개발자는 그러한 변경을 하지 않으려 들 것
- 이 문제를 해결하려면 테스트를 고려해서 설계해야 함
- 소프트웨어 설계의 첫 번째 규칙은 언제나 같다.
- 따라서 시스템과 테스트를 설계할 때 업무 규칙을 테스트할 수 있게 해야 함
테스트 API
- 이 목표를 달성하려면 테스트가 모든 업무 규칙을 검증하는 데 사용할 수 있도록 특화된 API 를 만들면 됨
- 이러한 API 는 보안 제약사항을 무시할 수 있으며, 데이터베이스와 같은 값비싼 자원은 건너뛰고, 시스템을 테스트 가능한 특정 상태로 강제하는 강력한 힘을 지녀야만 함
- 이 API 는 사용자 인터페이스가 사용하는 인터렉터와 인터페이스 어댑터들의 상위 집합이 될 것
- 테스트 API 는 테스트를 애플리케이션으로부터 분리할 목적으로 사용함
- 그리고 테스트 구조를 애플리케이션 구조로부터 결합을 분리하는 게 목표
구조적 결합
- 구조적 결합은 테스트 결합 중에서 가장 강하며, 가장 은밀하게 퍼져 나가는 유형
- 모든 사용 클래스에 테스트 클래스가 각각 존재하고, 또 모든 사용 메서드에 테스트 메서드 집합이 각각 존재하는 테스트 스위트가 있다고 가정해보자
- 이러한 테스트 스위트는 애플리케이션 구조에 강하게 결합되어 있음
- 테스트 API 의 역할은 애플리케이션 구조를 테스트로부터 숨기는 데 있음
- 이렇게 만들면 상용 코드를 리팩터링 하거나 진화시키더라도 테스트에는 전혀 영향을 주지 않음
- 또한 테스트를 리팩터링하거나 진화시킬 때도 상용 코드에는 전혀 영향을 주지 않음
- 이처럼 따로따로 진화할 수 있다는 점은 필수적임
- 시간이 지날수록 테스트는 계속해서 더 구체적으로 더 특화된 형태로 변할 것이고
- 반대로 상용 코드는 더 추상적으로 더 범용적인 형태로 변할 것이기 때문
- 하지만 조직 결합이 더 강하면 필수적인 진화 과정을 방해하거나 지연시킬 뿐만 아니라, 상용 코드의 범용성과 유연성이 충분히 좋아지지 못하게 막음
보안
- 테스트 API 가 지닌 강력한 힘을 운영 시스템에 배포하면 위험에 처할 수 있음
- 위험을 피하고 싶다면, 테스트 API 자체와 테스트 API 중 위험한 부분의 구현부는 독립적으로 배포할 수 있는 컴포넌트로 분리해야 함
결론
- 테스트는 시스템 외부에 있지 않음, 오히려 시스템의 일부
- 따라서 테스트에서 기대하는 안정성과 회귀의 이점을 얻을 수 있으려면 테스트는 잘 설계돼야만 함
- 테스트를 잘 설계하면, 깨지기 쉬워지지 않고, 유지보수하기 쉬워져 지속적으로 개발 가능해짐