////
Search
Duplicate
🧩

2. 단위 테스트란 무엇인가

2장에서 다루는 내용
단위 테스트란?
공유 의존성, 비공개 의존성, 휘발성 의존성 간의 차이점
단위 테스트의 두 분파: 고전파와 런던파
단위 테스트, 통합 테스트, E2E 테스트의 차이점
단위 테스트에는 고전파와 런던파 두 가지 견해가 있다.
고전파는 원론적으로 접근하는 방식이고 런던파는 런던의 한 프로그래밍 커뮤니티에서 시작되었다.

1. 단위 테스트의 정의

단위 테스트에는 수많은 정의 중, 공통적인 속성을 추려내면 다음과 같다.
작은 코드 조각을 검증하고
빠르게 수행하며
격리된 방식으로 처리하는 자동화된 테스트다.
고전파와 런던파가 나뉘는 부분은 마지막 격리된 방식에 대한 주제다.

1. 격리 문제에 대한 런던파의 접근

런던파에서는 하나의 클래스가 다른 클래스 또는 여러 클래스에서 의존하는 모든 의존성을 테스트 더블로 대체해야 한다고 한다.
즉 단위 테스트는 테스트 대상 클래스만 테스트하는 것이 격리된 방식이라고 생각한다.
이 방법의 이점은 다음과 같다.
테스트가 실패하면 코드의 어느 부분이 고장 났는지 확실히 알 수 있다.
같은 문제에 책임을 가지고 있는 객체 그래프를 분할할 수 있다.

2. 격리 문제에 대한 고전파의 접근

고전파에서는 단위 테스트의 테스트끼리 독립적으로 동작하는 것을 격리된 방식이라고 생각한다.
이 방법의 이점은 다음과 같다.
테스트가 서로 소통하고 실행 컨텍스트에 영향을 줄 수 있다.
데이터베이스, 파일 시스템 등 프로세스의 외부 의존성이 이러한 공유 상태의 대표적인 예다.

3. 공유 의존성, 비공개 의존성, 프로세스 외부 의존성, 휘발성 의존성

공유 의존성은 테스트 간에 공유되고 서로의 결과에 영향을 미칠 수 있는 수단을 제공하는 의존성이다.
정적 가변 필드(static mutable field)가 대표적인 예로 이런 필드의 변경 사항은 동일한 프로세스에서 실행되는 모든 단위 테스트에서 가시적이다.
데이터베이스도 이런 공유 의존성의 전형적인 예중 하나다.
휘발성 의존성은 다음 속성 중 하나를 나타내는 의존성이다.
개발자의 기기에 설치된 환경 외에 런타임 환경의 설정 및 구성을 요구한다. 데이터베이스와 API 등을 예로 들 수 있다. 추가 설정이 필요하며 시스템에 기본으로 설치되어있지 않다.
각 호출 시점에 따라 다른 결과를 제공하는 비결정적 동작을 포함한다.
난수 생성기나 현재 날짜와 시간을 반환하는 클래스들이 해당한다.
공유 의존성과 휘발성 의존성은 겹치는 부분이 있다.
데이터베이스는 공유 의존성이면서 휘발성 의존성이다. 파일 시스템은 휘발성이 아니다.
난수 생성기는 휘발성이지만 각 단위 테스트마다 별도의 인스턴스를 생성할 수 있으므로 공유 의존성이 아니다.
비공개 의존성은 공유하지 않는 의존성이다.
프로세스 외부 의존성은 애플리케이션 실행 프로세스 외부에서 실행되는 의존성이다.
예를 들어 데이터베이스는 프로세스 외부이면서 공유 의존성이다.
만약 각 테스트 실행 전 도커 컨테이너로 데이터베이스를 시작하면 테스트가 더 이상 동일한 데이터베이스 인스턴스로 동작하지 않기 때문에 프로세스 외부면서 비공개 의존성이다.

2. 단위 테스트의 런던파와 고전파

런던파는 테스트 대상 시스템에서 협력자 객체를 격리하는 것으로 본다.
고전파는 단위 테스트끼리 격리시키는 것으로 본다.
이를 조금 더 세분화해보자면 다음과 같은 세 가지 주요 주제에 대해 의견 차이가 존재한다.
격리 주체
단위의 크기
테스트 대역 사용 대상
격리 주체
단위의 크기
테스트 대역 사용 대상
런던파
단위
단일 클래스
불변 의존성 외 모든 의존성
고전파
단위 테스트
단일 클래스 또는 클래스 세트
공유 의존성

1. 고전파와 런던파가 의존성을 다루는 방법

런던파에서는 불변 의존성인 Enum 객체 Product를 제외한 나머지를 목으로 바꿔서 사용한다.
이러한 불변 객체를 Value Object라고 하며 주요 특징은 내용에 의해서만 식별된다는 것이다.
예를 들어 데이터베이스는 공유 의존성이며 내부의 상태는 모든 자동화된 테스트에서 공유한다.
협력자 vs 의존성
도메인 객체와 외부 의존성을 생각해보면 비유할 수 있을 듯 하다.

3. 고전파와 런던파의 비교

고전파와 런던파의 주요한 차이는 단위 테스트의 정의 중 세 번째인 격리 문제를 어떻게 다루는가에 있다.
즉 테스트할 단위의 처리와 의존성 취급에 대한 방법으로 넘어간다.
저자는 고전파를 더 선호한다 그 이유는 고품질의 테스트를 만들고 단위 테스트의 목표인 생산성을 유지하는 데 더 적합하기 때문이다.
그 이유는 취약성에 있는데, 목을 사용한 테스트는 고전파의 테스트보다 더 불안정한 경향이 있기 때문이다.
먼저 런던파의 장점을 하나씩 살펴보자.
입자성이 좋다. 한 번에 한 클래스만 확인한다.
클래스의 그래프가 커져도 테스트하기 쉽다. 모든 협력자는 테스트 더블로 대체되기 때문이다.
테스트가 실패했다면 어떤 기능이 실패했는지 확연하게 알 수 있다.

1. 한 번에 한 클래스만 테스트 하기

테스트는 비즈니스 로직을 검증해야 한다. 즉 문제 해결 영역에 의미가 있는 부분을 검증해야 한다.
동작 단위를 구현하는데 클래스가 얼마나 필요한지는 상관없다. 단위는 여러 클래스에 걸쳐 있거나 한 클래스에 있을수도 더욱이 아주 작은 메소드일 수도 있다.
입자성이 좋은 테스트를 목표로 하는 것은 도움이 되지 않는다.

2. 상호 연결된 클래스의 큰 그래프를 단위 테스트하기

실제로 생성된 협력자를 대신해 목을 사용하면 클래스를 쉽게 테스트할 수 있다.
고전파를 사용한다면 테스트 객체를 위한 의존성을 준비해주어야 하므로 비용이 많이 들 수 있다.
모두 사실이지만 이것은 잘못된 문제에 초점을 맞추고 있는 것이다.
테스트를 위해 어떻게 연결할 것인가 아닌 어떻게 연결을 단순화할 것인가에 대해서 생각하여야 한다.

3. 버그 위치 정확히 찾아내기

고전파 스타일의 단위 테스트를 채택한다면 하나의 클래스가 문제가 될 때 해당 클래스를 사용하는 모든 클래스에 문제가 생길 것이다.
이는 문제의 원인을 찾는 것이 어려워진다. 그러나 이는 우려할만은 하지만 큰 문제는 아니다.

4. 고전파와 런던파 사이의 다른 차이점

고전파와 런던파 사이에 남아있는 두 가지 차이점은 다음과 같다.
테스트 주도 개발을 통한 시스템 설계 방식
과도한 명세 문제
테스트 주도 개발을 통한 시스템 설계 방식
런던파의 단위테스트는 하향식 TDD로 이어지며 상위 계층부터 테스트를 진행해 내려가는 식이다.
이때 목을 사용해서 하위 계층을 대신한다. 테스트 시 SUT의 모든 협력자를 차단해 해당 협력자의 구현을 나중으로 미룰 수 있다.
고전파는 테스트에서 실제 객체를 다뤄야하기 때문에 되려 상향식으로 한다.
고전적 스타일에서는 도메인 모델부터 시작하여 클라이언트가 요구하는 값을 처리하기까지 계층을 추가한다.
과도한 명세 문제
두 스타일의 가장 큰 차이점으로 테스트 코드가 SUT의 구현 세부 사항에 결합되는 것이다.
런던파는 테스트가 더 자주 결합되는 편이다.

4. 두 분파의 통합 테스트

런던파와 고전파는 통합 테스트의 정의에도 차이가 있다.
런던파는 실제 의존성 객체를 사용하는 모든 테스트를 통합 테스트로 간주한다. 즉 고전파들이 작성한 단위 테스트를 런던파들은 통합 테스트로 여길 수도 있다는 것이다.
단위 테스트의 세 가지 속성
런던파는 다음과 같다.
작은 코드 조각을 검증하고 빠르게 수행하며 격리된 방식으로 처리한다
고전파는 다음과 같이 정의한다.
단일 동작 단위를 검증하고 빠르게 수행하며 다른 테스트와 별도로 처리한다
통합 테스트는 고전파 관점에서 단위 테스트의 속성을 위반하는 테스트다.
공유 의존성에 접근하는 테스트는 다른 테스트와 별도로 처리할 수 없다.
프로세스 외부 의존성에 접근하면 테스트를 빠르게 수행할 수 없다.
둘 이상의 동작 단위를 검증한다면 이는 통합 테스트다.

1. 통합 테스트의 일부인 E2E 테스트

통합 테스트는 공유 의존성, 프로세스 외부 의존성뿐 아니라 다른 팀이 개발한 코드 등과 통합해 작동하는지도 검증하는 테스트다.
E2E 테스트는 통합 테스트의 일부다. E2E 테스트도 코드가 프로세스 외부 종속성과 함께 어떻게 작동하는지 검증한다.
둘의 차이점은 E2E가 일반적으로 의존성을 더 많이 포함한다는 것이다.
일반적으로 통합 테스트는 프로세스 외부 의존성을 한 두개만 가지고 작동한다.
반면 E2E는 프로세스 외부 의존성을 대다수, 전부 가지고 작동한다. 따라서 E2E는 모든 외부 애플리케이션을 포함해 시스템을 클라이언트의 관점에서 검증하는 것을 의미한다.
UI 테스트, GUI 테스트, 기능 테스트와 같은 용어도 사용하지만 모두 동의어다.
애플리케이션이 데이터베이스, 파일 시스템, 결제 게이트웨이라는 세 가지 프로세스 외부 의존성으로 작동한다고 가정하자.
일반적인 통합 테스트는 데이터베이스와 파일 시스템만 포함하고 결제 게이트웨이는 테스트 더블로 대체한다.
데이터베이스와 파일 시스템은 완전히 제어할 수 있기 때문이다. 즉 프로세스 외부 의존성이 만약 외부 API라면 제어할 수 없으므로 의존하지말고 테스트 더블을 사용한다.
E2E 테스트는 유지 보수 측면에서 비용이 가장 많이 들기 때문에 모든 단위 테스트와 통합 테스트를 통과한 후 실행하는 것이 좋다.

요약

이 장에서는 단위 테스트의 정의를 다듬었다.
단일 동작 단위를 검증하고
빠르게 수행하며
다른 테스트와 별도로 처리한다.
격리 문제를 주로 논의하였는데, 이 논쟁으로 고전파, 런던파가 나뉜다. 이런 의견 차이는 단위가 무엇인가에 대한 관점과 테스트 대상 시스템의 의존성 처리 방식에 영향을 미친다.
런던파는 테스트 대상 단위를 서로 분리해야 한다고 한다. 테스트 대상 단위는 코드 블록 또는 보통 단위 클래스다. 불변 의존성을 제외한 모든 의존성을 테스트 더블로 처리해야 한다.
고전파는 단위가 아니라 단위 테스트를 서로 분리해야 한다고 한다. 또한 테스트 대상 단위는 코드 단위가 아니라 동작 단위다. 따라서 공유 의존성만 테스트 더블로 대체해야 한다.
런던파는 더 나은 입자성, 상호 연결된 클래스의 큰 그래프가 제공하는 의존성 주입의 골치아픔 그리고 테스트 실패 후 버그가 있는 기능을 쉽게 찾을 수 있는 편의성을 제공한다.
이는 처음에 매력적으로 보이나 몇 가지 문제가 있다.
테스트 대상 클래스에 대한 관점이 잘못되었다. 코드 단위가 아닌 동작 단위를 테스트해야 한다.
코드 조각을 단위 테스트하기 힘들다는 것은 설계에 문제가 있다는 반증이다. 또한 테스트 더블을 사용한다고 해도 문제를 해결하는 것이 아니라 회피하는 것이다.
테스트 실패 후 어떤 기능에 버그가 있는지 판단하는 것이 도움은 되지만 종종 버그의 원인을 알고 있기 때문에 큰 장점은 아니다.
런던파의 가장 큰 문제는 과잉 면세, 즉 SUT 세부 구현에 결합된 테스트 문제이다.
통합 테스트는 단위 테스트 기준 중 하나 이상을 위반하는 테스트다.
E2E 테스트는 통합 테스트의 일부다. 클라이언트의 관점에서 시스템을 검증한다. E2E 테스트는 애플리케이션과 함께 작동하는 프로세스 외부 의존성의 전부 또는 대부분에 접근한다.
저자의 책추천
고전파를 따르는 명저로 켄트 벡의 TDD를 추천한다고 한다.
런던파에 대한 자세한 내용은 스티브 프리먼과 냇 프라이스의 Growing Object-Oriented Software, Guided By Tests
의존성 작업에 대해 자세하게 알고 싶다면 스티븐 반 듀르센과 마크 시먼의 Dependency Injection: Principles, Pratices, Patterns