[xUnit 테스트 패턴] 18장 - 테스트 전략 패턴
18장 - 테스트 전략 패턴
기록 테스트
- 테스트 도구로 기록한 후 다시 재생하는 방식으로 테스트 자동화
동작 원리 : 기록 테스트
- SUT가 작업하는 동안 발생하는 상호작용을 도구를 통해 모니터링
- SUT가 사용자에게 보내는 메시지와 사용자의 응답을 기록
- 기록이 끝나면 파일로 저장
- 도구의 재생 기능으로 이전에 저장해 둔 세션 선택
- 도구로 SUT 시작시키고 SUT의 응답에 맞춰 기록해뒀던 사용자의 입력을 넘긴다.
- SUT의 출력 값을 기록 세션의 출력 값과 비교
- ex) 로컬 DB 사용?
변형 : 리팩토링한 기록 테스트
- 기록하고 리팩토링한 후 재생하기
- 단위 테스트 안에서 SUT와 상호작용하기 위해 테스트 유틸리티 메소드를 사용하는 것과 동일함.
구현 : 기록 테스트
변형 : 외부화된 테스트 기록
- 직접 수정할 수 있고 이해하기 쉬운 테스트 기록 파일 형식을 쓰는 도구를 선택해야 한다. 그런 다음 테스트 내용을 직접 작성해야 한다.
변형 : 내장 테스트 기록 기능
스크립트 기반 테스트
- 테스트 프로그램을 직접 작성해 테스트를 자동화한다.
동작 원리 : 스크립트 기반 테스트
- SUT의 기능을 실행하기 위해 SUT와 상호작용하는 테스트 프로그램을 작성해 테스트를 자동화한다.
테스트 스크립트
라 부른다.
언제 쓸 것인가 : 스크립트 기반 테스트
- 코드의 모든 경로를 실행시켜 볼 수 있다.
- 자동 스토리테스트로 개발을 진행하는 경우에는 꼭 써야 한다.
구현 : 스크립트 기반 테스트
- 테스트 자동 프레임워크(xUnit)로 스크립트 기반 테스트 작성하는 걸 선호
- 테스트 메소드를 반복되는 테스트이면서 자체 검사 테스트로 구현
- ex) 일반적으로 테스트 클래스내에서 픽스처 생성 메서드를 만드는 방법
데이터 주도 테스트
- 테스트에 필요한 모든 정보를 데이터 파일에 저장한 후 인터프리터를 만들어 파일에서 정보를 읽어와 테스트를 실행
언제 쓸 것인가 : 데이터 주도 테스트
- 기록 테스트와 스크립트 기반 테스트를 대체할 수 있는 방식
- 설정 데이터를 변경해서 테스트 하기 좋음
구현 : 데이터 주도 테스트
- 테스트를 데이터 주도 테스트 인터프리터와 데이터 주도 테스트 파일 두 부분으로 나눈다.
- 데이터 주도 테스트 파일을 버전 컨트롤 관리 저장소에 저장한다.
- 테스트 성공과 실패 두가지로 나눠서 데이터 주도 테스트 파일 관리한다.
리팩토링 : 데이터 주도 테스트
- 인자를 받는 테스트의 공통 로직을 뽑아내 데이터 주도 테스트 인터프리터를 만들고 인자 값을 누구나 수정할 수 있는 하나의 데이터 파일로 만든다.
예제 : 데이터 주도 테스트
테스트 자동 프레임워크
- 테스트 로직 실행에 필요한 모든 매커니즘을 제공하는 프레임워크를 통해 테스트 작성자는 테스트용 로직만 개발할 수 있게 한다.
자동화 프레임워크를 사용하는 이유
- 완전 자동 테스트를 구축하는데 드는 비용을 많이 줄일 수 있다.
- 테스트들을 같은 파일에서 쉽게 실행할 수 있고, 테스트 결과를 한 번에 볼 수 있다.
구현 : 테스트 자동 프레임워크
최소 픽스처
- 모든 테스트마다 가장 작고 단순한 픽스처를 쓴다.
최소 픽스처를 사용하는 이유
- 문서로서의 테스트를 만들고 느린 테스트를 피하기 위해서
구현 : 최소 픽스처
- 테스트가 검증해야 하는 기능을 표현하는 데 꼭 필요한 객체들만 포함한다.
- 필요 없는 객체 제거 방법
- 숨긴다.
- 더미 객체를 넘기거나 엔티티 체인 스니핑(Entity Chanin Snipping)으로 객체를 더 이상 필요하지 않게 만든다.
표준 픽스처
- 여러 테스트에서 같은 테스트 픽스처 설계를 재사용한다.
동작 원리 : 표준 픽스처
- 테스트 프로세스 초반에 여러 테스트에서 사용될 표준 픽스처를 미리 설계한다.
- 공유 픽스처 != 표준 픽스처
- 표준 픽스처는 픽스처 설계의 재사용에 초점이 맞춰져 있고 픽스처 생성 시점이나 가시성 여부는 고려하지 않는다.
구현 : 표준 픽스처
- 신선한 픽스처로 구현
- 표준 픽스처를 생성하는 테스트 유틸리티 메소드 정의
- 암묵적 설치
- setUp 메소드에 픽스처 생성 로직으로 사용
- 공유 픽스처로 사용
- 스위트 픽스처 설치, 지연 설치, 설치 데코레이터 중 선택해서 사용
신선한 픽스처
- 테스트별로 전용 테스트 픽스처를 새로 만들어 쓴다.
- 변덕스러운 테스트 방지, 문서로서의 테스트 가능
왜 픽스처 지속이 필요한가
신선한 픽스처 설치
- 간단 - 인라인 설치
- 복잡 - 위임 설치 or 암묵적 설치
변형 : 1회용 신선한 픽스처
- 지역변수, 인스턴스 변수 -> 가비지 컬렉션 해체로 가능
- 표준 픽스처 -> 테스트 메소드를 실행하기 전에 생성된다면 신선한 픽스처가 될 수 있다.
변형 : 지속되는 신선한 픽스처
- 지속되는 신선한 픽스처가 있다면 아래와 같은 픽스처 해체 방법을 사용해야 한다.
- 인라인 해체, 암묵적 해체, 위임 해체, 자동 해체
- 픽스처 해체를 피하려면 픽스처별 고유한 별개의 생성 값 사용
- 테스트 독립적으로 유지 못함.
- 서로 반응하는 테스트와 반복 안 되는 테스트가 됨.
지속되는 신선한 픽스처는 충분히 이해 하기 전까지는 안 쓰는게 나음.
공유 픽스처
- 같은 테스트 픽스처 객체를 여러 테스트에서 재사용한다.
- 테스트를 좀 더 빠르게 실행 가능
동작 원리 : 공유 픽스처
남겨놓은
픽스처를 재사용
- 여러 테스트 실행에서 재사용되는 미리 만든 픽스처
- 같은 테스트 실행 안에서 어떤 테스트에서 생성해 놓은 것을 다른 테스트에서 재사용하는 픽스처
언제 쓸 것인가 : 공유 픽스처
변형 : 느린 테스트
테스트마다 새로운 픽스처를 만들기 어려울 때 공유 픽스처 사용
- 발생원인
- 테스트마다 새로운 픽스처를 생성하는 데 너무 오래 걸릴 때 느린 테스트가 된다.
변형 : 점진적인 테스트
길고 복잡한 여러 단계의 작업이 이전 작업에 의존할 경우 공유 픽스처 사용
- 발생원인
- 고객 테스트 : 워크플로우 형태
- 단위 테스트 : 같은 객체에 여러 메소드를 순서대로 호출하는 형태
- 대안
- 동작별로 별도의 테스트 메소드를 만든 후 이전 테스트가 공유 픽스처에 작업해둔 걸 기반으로 실행 -> 엮인 테스트
- 엮인 테스트
- 단점 : 도중에 실패하면 다른 테스트들 모두 실패되고 진짜 실패한 이유를 알기 어렵다.
- 해결책
- 테스트 메소드 시작마다 몇 개의 보호 단언문으로 테스트 메소드가 픽스처의 상태에 대해 가정하고 있는 바를 문서화 하기
- 보호 단언문이 실패하면 먼저 실패한 테스트를 보거나 실행되는 순서 확인해볼 수 있다.
구현 : 공유 픽스처
- 생성한 픽스처에 대한 레퍼런스를 유지해야 함.
- 이미 만들어져 있는 픽스처를 찾을 수 있고 사용할 수 있게 해주기 위해서..
- 이 레퍼런스를 필요로 하는 모든 테스트에서 접근할 수 있게 해준다.
변형 : 실행별 픽스처
- 가장 단순한 공유 픽스처의 형태
- 테스트를 시작할 때 픽스처를 설치하고 같이 실행되는 모든 테스트에서 이 픽스처를 공유할 수 있게 한다.
- 테스트 실행이 끝날 때 픽스처도 같이 삭제 됨
- setUp 메소드와 tearDown 메소드등의 테스트 픽스처 레지스트리로 픽스처에 접근
변형 : 불변 공유 픽스처
- 공유 픽스처를 변경하면 변덕스러운 테스트가 생길 수 있다.
- 테스트별 픽스처는 불변 공유 픽스처 위에서 신선한 픽스처로 생성하면 된다.
시작 예제 : 공유 픽스처
- 암묵적 설치(Implicit Setup)로 테스트 픽스처 설치
- setUp 메소드 사용
뒷문 조작
- (데이터베이스를 직접 접근하는 식으로) 뒷문으로 테스트 픽스처를 설치하거나 결과를 검증한다.
동작 원리 : 뒷문 조작
- 데이터베이스, 레지스트리 객체나 파일 시스템, 다른 클래스나 객체 등등이 뒷문 역할을 한다.
- 테스트 방법
- 의존 컴포넌트(DOC)에 실제 객체 대신 테스트 대역을 적절하게 사용
언제 쓸 것인가 : 뒷문 조작
- 사용하는 경우
- 뒷문 조작의 단점
- SUT에 대한 설계에 테스트나 테스트 유틸리티 메소드가 매우 강하게 결합된다.
- 애매한 테스트가 되기 쉽다.
- 대안
- 뒷문 조작을 쓰는 레이어 횡단 테스트로 데이터베이스에 직접 내용을 설치하고 검증
읽기
테스트
뒷문 설치
로 데이터베이스에 내용을 설치 후 SUT로 데이터 읽음
쓰기
테스트
- 테스트에서 시스템이 특정 객체를 쓰게 만든 후
뒷문 검증
으로 데이터베이스의 내용을 검증
변형 : 뒷문 설치
불가능한
설정을 쉽게 만들 수 있다.
- ex) 원하는 데이터를 테이블에 집어넣는식으로..
변형 : 뒷문 검증
- 고객 테스트에 가장 적합하다.
- SQL 같은 표준 API나 데이터 내보내기로 데이터베이스의 객체를 얻어온 후 파일 비교 유틸리티 프로그램으로 검사 가능
변형 : 뒷문 해체
구현 : 뒷문 조작
변형 : 데이터베이스 채우기 스크립트
- SUT를 실행하기 전에 데이터베이스에 직접 데이터를 집어넣는 것
- 구조나 데이터의 의미가 바뀔 때마다 유지 보수 해줘야 함
- 유지 비용 늘어남
변형 : 데이터 로더
- SUT의 데이터 저장 공간에 데이터를 로딩하기 위한 별도의 프로그램
- SUT와 데이터 로더의 다른점
- 테스트에서만 사용
- SUT가 데이터를 받는 방식과 상관없이 파일에서 데이터를 읽어온다.
- SUT의 여러
입력 값 검사
루틴을 우회한다.
변형 : 데이터베이스 추출 스크립트
- 테스트용 데이터베이스에서 데이터베이스 스크립트로 데이터 추출해서 검증
변형 : 데이터 반환기
- 데이터 로더와 비슷함
- 데이터를
물어와
테스트에서 기대 결과 값과 비교할 수 있게 한다.
변형 : 뒷문으로서의 테스트 대역
- 뒷문 조작의 일반적인 형태 : DOC를 테스트 대역으로 바꾸는 것
- SUT가 미리 데이터를 로딩해 놓은 가짜 객체와 상호작용하게 만들기
- 모의 객체나 테스트 스텁 사용하기
레이어 테스트
- 레이어 아키텍처의 각 레이어별로 테스트를 작성한다.
- 프레젠테이션(사용자 인터페이스)레이어, 비즈니스 로직 레이어, 도메인 레이어, 지속 레이어(persistence layer) 등등
언제 쓸 것인가 : 레이어 테스트
- 프로젝트 팀을 여러 팀으로 분리할 때 유용
- 레이어 인터페이스의 의미를 문서화하고 고정시킬 수 있는 좋은 방법
하향식
테스트도 몇 개 작성해둬서 통합테스트 진행으로 검증하는게 좋다.
- 한두 개 정도의 기본 시나리오만 하향식 테스트로 검증
변형 : 프레젠테이션 레이어 테스트
- 프레젠테이션 레이어의 로직을 프레젠테이션 프레임워크와는 독립적으로 테스트할 수 있게 설계해야 한다.
- ex) 대강 만든 다이얼로그
변형 : 서비스 레이어 테스트
- 서비스 레이어에는 일반적으로 대부분의 단위 테스트와 컴포넌트 테스트들이 몰려있다.
- 고객 테스트로 테스트하기 어려운 것들을 서비스 레이어에서 할 수 있다.
변형 : 영속성 레이어 테스트
- 뒷문 조작으로 테스트하기 전에 데이터베이스의 내용을 채워 넣거나 테스트가 끝난 후 데이터베이스 내용을 검증할 수 있다.
변형 : 피하 테스트
- 프레젠테이션 레이어를 건너뛰어 서비스 레이어로 직접 접근하는 레이어 테스트의 변형
- 프레젠테이션을 제외한 모든 부분을 테스트 할 수 있다.
- 서비스 레이어 테스트처럼 엄격하게 관심의 분리를 하지 않아도 되므로 테스트 용이성을 고려하지 않은 기존 프로그램에 테스트 도입하기 유용함
변형 : 컴포넌트 테스트
- 레이어 테스트의 가장 일반적인 형태
작은 레이어
역할을 하는 개별 컴포넌트들이 합쳐진 형태
구현 : 레이어 테스트
왕복 테스트
- 테스트하고 싶은 레이어가 아래 레이어와 완전히 격리돼 있는지 여부와는 상관없이 작성 가능
- 실제 컴포넌트를 두고 간접적으로 실행할 수 있고, 가짜 객체로 바꿔줄수도 있다.
간접 입력 제어하기
- 시스템의 하위 레이어를 테스트 스텁으로 교체 할 수 있다.
- 클라이언트 레이어의 요청에 따라
저장된
결과를 리턴
- 기대 예외 테스트에 좋음
- 뒷문 조작으로 간접 입력 설치해주는 방법도 있다.
간접 출력 검증
- SUT 하위 레이어에 있는 컴포넌트를 모의 객체나 테스트 스파이로 교체한다.
- 대안
- SUT가 실행된 다음에 SUT의 간접 출력을 뒷문 조작으로 검증하는 방법