[xUnit 테스트 패턴] 18장 - 테스트 전략 패턴


18장 - 테스트 전략 패턴

기록 테스트

  • 테스트 도구로 기록한 후 다시 재생하는 방식으로 테스트 자동화

동작 원리 : 기록 테스트

  • SUT가 작업하는 동안 발생하는 상호작용을 도구를 통해 모니터링
  • SUT가 사용자에게 보내는 메시지와 사용자의 응답을 기록
  • 기록이 끝나면 파일로 저장
  • 도구의 재생 기능으로 이전에 저장해 둔 세션 선택
  • 도구로 SUT 시작시키고 SUT의 응답에 맞춰 기록해뒀던 사용자의 입력을 넘긴다.
  • SUT의 출력 값을 기록 세션의 출력 값과 비교
  • ex) 로컬 DB 사용?

변형 : 리팩토링한 기록 테스트

  • 기록하고 리팩토링한 후 재생하기
  • 단위 테스트 안에서 SUT와 상호작용하기 위해 테스트 유틸리티 메소드를 사용하는 것과 동일함.

구현 : 기록 테스트

변형 : 외부화된 테스트 기록

  • 직접 수정할 수 있고 이해하기 쉬운 테스트 기록 파일 형식을 쓰는 도구를 선택해야 한다. 그런 다음 테스트 내용을 직접 작성해야 한다.

변형 : 내장 테스트 기록 기능

  • SUT 안에 기록 테스트 기능을 구현

스크립트 기반 테스트

  • 테스트 프로그램을 직접 작성해 테스트를 자동화한다.

동작 원리 : 스크립트 기반 테스트

  • SUT의 기능을 실행하기 위해 SUT와 상호작용하는 테스트 프로그램을 작성해 테스트를 자동화한다.
  • 테스트 스크립트라 부른다.

언제 쓸 것인가 : 스크립트 기반 테스트

  • 코드의 모든 경로를 실행시켜 볼 수 있다.
  • 자동 스토리테스트로 개발을 진행하는 경우에는 꼭 써야 한다.

구현 : 스크립트 기반 테스트

  • 테스트 자동 프레임워크(xUnit)로 스크립트 기반 테스트 작성하는 걸 선호
  • 테스트 메소드를 반복되는 테스트이면서 자체 검사 테스트로 구현
  • ex) 일반적으로 테스트 클래스내에서 픽스처 생성 메서드를 만드는 방법

데이터 주도 테스트

  • 테스트에 필요한 모든 정보를 데이터 파일에 저장한 후 인터프리터를 만들어 파일에서 정보를 읽어와 테스트를 실행

언제 쓸 것인가 : 데이터 주도 테스트

  • 기록 테스트와 스크립트 기반 테스트를 대체할 수 있는 방식
  • 설정 데이터를 변경해서 테스트 하기 좋음
    • 별도 소스 커밋이 필요하지 않음

구현 : 데이터 주도 테스트

  • 테스트를 데이터 주도 테스트 인터프리터와 데이터 주도 테스트 파일 두 부분으로 나눈다.
  • 데이터 주도 테스트 파일을 버전 컨트롤 관리 저장소에 저장한다.
  • 테스트 성공과 실패 두가지로 나눠서 데이터 주도 테스트 파일 관리한다.

리팩토링 : 데이터 주도 테스트

  • 인자를 받는 테스트의 공통 로직을 뽑아내 데이터 주도 테스트 인터프리터를 만들고 인자 값을 누구나 수정할 수 있는 하나의 데이터 파일로 만든다.

예제 : 데이터 주도 테스트

  • XML, CSV, Fit 프레임워크 등등

테스트 자동 프레임워크

  • 테스트 로직 실행에 필요한 모든 매커니즘을 제공하는 프레임워크를 통해 테스트 작성자는 테스트용 로직만 개발할 수 있게 한다.

자동화 프레임워크를 사용하는 이유

  • 완전 자동 테스트를 구축하는데 드는 비용을 많이 줄일 수 있다.
  • 테스트들을 같은 파일에서 쉽게 실행할 수 있고, 테스트 결과를 한 번에 볼 수 있다.

구현 : 테스트 자동 프레임워크

  • 로봇 사용자 테스트 툴
    • 위젯 구성기 플러그인
  • 스크립트 기반 테스트
    • xUnit
    • 데이터 주도 테스트

최소 픽스처

  • 모든 테스트마다 가장 작고 단순한 픽스처를 쓴다.

최소 픽스처를 사용하는 이유

  • 문서로서의 테스트를 만들고 느린 테스트를 피하기 위해서

구현 : 최소 픽스처

  • 테스트가 검증해야 하는 기능을 표현하는 데 꼭 필요한 객체들만 포함한다.
  • 필요 없는 객체 제거 방법
    • 숨긴다.
    • 더미 객체를 넘기거나 엔티티 체인 스니핑(Entity Chanin Snipping)으로 객체를 더 이상 필요하지 않게 만든다.

표준 픽스처

  • 여러 테스트에서 같은 테스트 픽스처 설계를 재사용한다.

동작 원리 : 표준 픽스처

  • 테스트 프로세스 초반에 여러 테스트에서 사용될 표준 픽스처를 미리 설계한다.
  • 공유 픽스처 != 표준 픽스처
  • 표준 픽스처는 픽스처 설계의 재사용에 초점이 맞춰져 있고 픽스처 생성 시점이나 가시성 여부는 고려하지 않는다.

구현 : 표준 픽스처

  • 신선한 픽스처로 구현
    • 표준 픽스처를 생성하는 테스트 유틸리티 메소드 정의
  • 암묵적 설치
    • setUp 메소드에 픽스처 생성 로직으로 사용
  • 공유 픽스처로 사용
    • 스위트 픽스처 설치, 지연 설치, 설치 데코레이터 중 선택해서 사용

신선한 픽스처

  • 테스트별로 전용 테스트 픽스처를 새로 만들어 쓴다.
  • 변덕스러운 테스트 방지, 문서로서의 테스트 가능

왜 픽스처 지속이 필요한가

  • 픽스처 지속이 되는 경우
    • 데이터베이스 사용
    • 테스트 제어
      • 인스턴스 변수 사용
      • 클래스 변수 사용

신선한 픽스처 설치

  • 간단 - 인라인 설치
  • 복잡 - 위임 설치 or 암묵적 설치

변형 : 1회용 신선한 픽스처

  • 지역변수, 인스턴스 변수 -> 가비지 컬렉션 해체로 가능
  • 표준 픽스처 -> 테스트 메소드를 실행하기 전에 생성된다면 신선한 픽스처가 될 수 있다.

변형 : 지속되는 신선한 픽스처

  • 지속되는 신선한 픽스처가 있다면 아래와 같은 픽스처 해체 방법을 사용해야 한다.
    • 인라인 해체, 암묵적 해체, 위임 해체, 자동 해체
  • 픽스처 해체를 피하려면 픽스처별 고유한 별개의 생성 값 사용
    • 테스트 독립적으로 유지 못함.
    • 서로 반응하는 테스트와 반복 안 되는 테스트가 됨.
  • 지속되는 신선한 픽스처는 충분히 이해 하기 전까지는 안 쓰는게 나음.

공유 픽스처

  • 같은 테스트 픽스처 객체를 여러 테스트에서 재사용한다.
  • 테스트를 좀 더 빠르게 실행 가능

동작 원리 : 공유 픽스처

  • 남겨놓은 픽스처를 재사용
    • 여러 테스트 실행에서 재사용되는 미리 만든 픽스처
    • 같은 테스트 실행 안에서 어떤 테스트에서 생성해 놓은 것을 다른 테스트에서 재사용하는 픽스처

언제 쓸 것인가 : 공유 픽스처

변형 : 느린 테스트

  • 테스트마다 새로운 픽스처를 만들기 어려울 때 공유 픽스처 사용
  • 발생원인
    • 테스트마다 새로운 픽스처를 생성하는 데 너무 오래 걸릴 때 느린 테스트가 된다.
      • 데이터베이스에서 테스트

변형 : 점진적인 테스트

  • 길고 복잡한 여러 단계의 작업이 이전 작업에 의존할 경우 공유 픽스처 사용
  • 발생원인
    • 고객 테스트 : 워크플로우 형태
    • 단위 테스트 : 같은 객체에 여러 메소드를 순서대로 호출하는 형태
  • 대안
    • 동작별로 별도의 테스트 메소드를 만든 후 이전 테스트가 공유 픽스처에 작업해둔 걸 기반으로 실행 -> 엮인 테스트
  • 엮인 테스트
    • 단점 : 도중에 실패하면 다른 테스트들 모두 실패되고 진짜 실패한 이유를 알기 어렵다.
    • 해결책
      • 테스트 메소드 시작마다 몇 개의 보호 단언문으로 테스트 메소드가 픽스처의 상태에 대해 가정하고 있는 바를 문서화 하기
      • 보호 단언문이 실패하면 먼저 실패한 테스트를 보거나 실행되는 순서 확인해볼 수 있다.

구현 : 공유 픽스처

  • 생성한 픽스처에 대한 레퍼런스를 유지해야 함.
    • 이미 만들어져 있는 픽스처를 찾을 수 있고 사용할 수 있게 해주기 위해서..
    • 이 레퍼런스를 필요로 하는 모든 테스트에서 접근할 수 있게 해준다.

변형 : 실행별 픽스처

  • 가장 단순한 공유 픽스처의 형태
  • 테스트를 시작할 때 픽스처를 설치하고 같이 실행되는 모든 테스트에서 이 픽스처를 공유할 수 있게 한다.
    • 테스트 실행이 끝날 때 픽스처도 같이 삭제 됨
  • setUp 메소드와 tearDown 메소드등의 테스트 픽스처 레지스트리로 픽스처에 접근

변형 : 불변 공유 픽스처

  • 공유 픽스처를 변경하면 변덕스러운 테스트가 생길 수 있다.
    • 공유 픽스처를 불변으로 만들어서 해결
  • 테스트별 픽스처는 불변 공유 픽스처 위에서 신선한 픽스처로 생성하면 된다.
    • 변경이 필요한 부분은 신선한 픽스처로…

시작 예제 : 공유 픽스처

  • 암묵적 설치(Implicit Setup)로 테스트 픽스처 설치
  • setUp 메소드 사용

뒷문 조작

  • (데이터베이스를 직접 접근하는 식으로) 뒷문으로 테스트 픽스처를 설치하거나 결과를 검증한다.

동작 원리 : 뒷문 조작

  • 데이터베이스, 레지스트리 객체나 파일 시스템, 다른 클래스나 객체 등등이 뒷문 역할을 한다.
  • 테스트 방법
    • 의존 컴포넌트(DOC)에 실제 객체 대신 테스트 대역을 적절하게 사용

언제 쓸 것인가 : 뒷문 조작

  • 사용하는 경우
    • 기본적인 CRUD 동작 테스트
  • 뒷문 조작의 단점
    • SUT에 대한 설계에 테스트나 테스트 유틸리티 메소드가 매우 강하게 결합된다.
    • 애매한 테스트가 되기 쉽다.
  • 대안
    • 뒷문 조작을 쓰는 레이어 횡단 테스트로 데이터베이스에 직접 내용을 설치하고 검증
    • 읽기 테스트
      • 뒷문 설치로 데이터베이스에 내용을 설치 후 SUT로 데이터 읽음
    • 쓰기 테스트
      • 테스트에서 시스템이 특정 객체를 쓰게 만든 후 뒷문 검증으로 데이터베이스의 내용을 검증

변형 : 뒷문 설치

  • 불가능한 설정을 쉽게 만들 수 있다.
    • ex) 원하는 데이터를 테이블에 집어넣는식으로..

변형 : 뒷문 검증

  • 고객 테스트에 가장 적합하다.
  • SQL 같은 표준 API나 데이터 내보내기로 데이터베이스의 객체를 얻어온 후 파일 비교 유틸리티 프로그램으로 검사 가능

변형 : 뒷문 해체

  • 테이블 삭제 해체나 트랜잭션 롤백 해체 사용

구현 : 뒷문 조작

변형 : 데이터베이스 채우기 스크립트

  • SUT를 실행하기 전에 데이터베이스에 직접 데이터를 집어넣는 것
    • 구조나 데이터의 의미가 바뀔 때마다 유지 보수 해줘야 함
    • 유지 비용 늘어남

변형 : 데이터 로더

  • SUT의 데이터 저장 공간에 데이터를 로딩하기 위한 별도의 프로그램
  • SUT와 데이터 로더의 다른점
    • 테스트에서만 사용
    • SUT가 데이터를 받는 방식과 상관없이 파일에서 데이터를 읽어온다.
      • ex) 텍스트파일, csv, xml 등등
    • SUT의 여러 입력 값 검사 루틴을 우회한다.

변형 : 데이터베이스 추출 스크립트

  • 테스트용 데이터베이스에서 데이터베이스 스크립트로 데이터 추출해서 검증

변형 : 데이터 반환기

  • 데이터 로더와 비슷함
  • 데이터를 물어와테스트에서 기대 결과 값과 비교할 수 있게 한다.

변형 : 뒷문으로서의 테스트 대역

  • 뒷문 조작의 일반적인 형태 : DOC를 테스트 대역으로 바꾸는 것
    • SUT가 미리 데이터를 로딩해 놓은 가짜 객체와 상호작용하게 만들기
    • 모의 객체나 테스트 스텁 사용하기

레이어 테스트

  • 레이어 아키텍처의 각 레이어별로 테스트를 작성한다.
    • 프레젠테이션(사용자 인터페이스)레이어, 비즈니스 로직 레이어, 도메인 레이어, 지속 레이어(persistence layer) 등등

언제 쓸 것인가 : 레이어 테스트

  • 프로젝트 팀을 여러 팀으로 분리할 때 유용
  • 레이어 인터페이스의 의미를 문서화하고 고정시킬 수 있는 좋은 방법
  • 하향식 테스트도 몇 개 작성해둬서 통합테스트 진행으로 검증하는게 좋다.
    • 한두 개 정도의 기본 시나리오만 하향식 테스트로 검증

변형 : 프레젠테이션 레이어 테스트

  • 프레젠테이션 레이어의 로직을 프레젠테이션 프레임워크와는 독립적으로 테스트할 수 있게 설계해야 한다.
  • ex) 대강 만든 다이얼로그

변형 : 서비스 레이어 테스트

  • 서비스 레이어에는 일반적으로 대부분의 단위 테스트와 컴포넌트 테스트들이 몰려있다.
  • 고객 테스트로 테스트하기 어려운 것들을 서비스 레이어에서 할 수 있다.

변형 : 영속성 레이어 테스트

  • 뒷문 조작으로 테스트하기 전에 데이터베이스의 내용을 채워 넣거나 테스트가 끝난 후 데이터베이스 내용을 검증할 수 있다.

변형 : 피하 테스트

  • 프레젠테이션 레이어를 건너뛰어 서비스 레이어로 직접 접근하는 레이어 테스트의 변형
  • 프레젠테이션을 제외한 모든 부분을 테스트 할 수 있다.
  • 서비스 레이어 테스트처럼 엄격하게 관심의 분리를 하지 않아도 되므로 테스트 용이성을 고려하지 않은 기존 프로그램에 테스트 도입하기 유용함

변형 : 컴포넌트 테스트

  • 레이어 테스트의 가장 일반적인 형태
  • 작은 레이어 역할을 하는 개별 컴포넌트들이 합쳐진 형태

구현 : 레이어 테스트

왕복 테스트

  • 테스트하고 싶은 레이어가 아래 레이어와 완전히 격리돼 있는지 여부와는 상관없이 작성 가능
  • 실제 컴포넌트를 두고 간접적으로 실행할 수 있고, 가짜 객체로 바꿔줄수도 있다.

간접 입력 제어하기

  • 시스템의 하위 레이어를 테스트 스텁으로 교체 할 수 있다.
    • 클라이언트 레이어의 요청에 따라 저장된 결과를 리턴
  • 기대 예외 테스트에 좋음
  • 뒷문 조작으로 간접 입력 설치해주는 방법도 있다.

간접 출력 검증

  • SUT 하위 레이어에 있는 컴포넌트를 모의 객체나 테스트 스파이로 교체한다.
  • 대안
    • SUT가 실행된 다음에 SUT의 간접 출력을 뒷문 조작으로 검증하는 방법