사전 중간 범위 테스트
개념

중간 범위 테스트

gabury1고친 사람 github-actions[bot]

중간 범위 테스트는 코드의 부분 몇 개가 서로 맞게 주고받는지 확인합니다. 확인하는 코드의 양이 좁은 범위 테스트와 넓은 범위 테스트의 사이입니다. 서버 코드가 데이터베이스에 값을 쓰고 다시 읽어 오는지 보는 테스트가 대표입니다. 보통 통합 테스트라고 부르는 것이 이것입니다.

쉽고 빠른 이해

무슨 일을 하나 — 부분 둘을 진짜로 잇습니다. 그 사이의 주고받기만 확인합니다. 회원을 저장하는 코드가 데이터베이스에 회원을 넣고 다시 꺼내 오는지 보는 식입니다.

왜 하나 — 부분 하나만 떼어 검사할 때는 상대 부분을 가짜로 바꿔 끼웁니다. 가짜는 테스트를 쓴 사람이 짐작한 대로만 답합니다. 짐작이 틀렸는지는 진짜와 이어 봐야 압니다.

어떻게 도나

  1. 확인할 부분 둘을 정합니다
  2. 둘은 진짜로 띄우고 나머지는 가짜로 둡니다
  3. 한쪽에서 요청을 보낸 뒤 다른 쪽이 남긴 결과를 확인합니다

대가 — 부분 하나만 보는 테스트보다 느립니다. 테스트끼리 같은 데이터베이스를 쓰면 앞 테스트가 남긴 데이터가 뒤 테스트를 흔듭니다.

언제 쓰나 — 내 코드가 데이터베이스나 다른 서비스와 맞닿는 곳마다 둡니다. 계산의 갈래는 부분 하나만 보는 테스트에 맡깁니다.

상세

이 절은 회원 가입 기능 하나를 두고 중간 범위 테스트를 봅니다. 등장하는 부분은 둘입니다. 회원을 저장하고 꺼내는 클래스, 그리고 회원이 실제로 쌓이는 데이터베이스입니다.

배관공이 새 관 두 개를 이었다고 해 봅시다. 관 하나하나는 공장에서 이미 검사를 받았습니다. 배관공은 이음매에 물을 흘려 봅니다. 새는 곳이 없는지만 확인합니다. 집 안 수도꼭지를 전부 틀어 보지는 않습니다.

테스트 범위라는 잣대

테스트 범위는 테스트 하나가 맞는지 확인하는 코드의 양입니다. 확인하는 코드가 적을수록 범위가 좁습니다. 팀마다 테스트를 부르는 이름이 다릅니다. 이 잣대로 재면 같은 테스트를 같은 말로 부를 수 있습니다.

범위는 세 칸으로 나눕니다. 아래 표는 칸마다 무엇을 확인하는지 회원 가입 기능을 예로 들어 보입니다.

범위 확인하는 것 회원 가입에서의 예 흔히 부르는 이름
좁은 범위 테스트 클래스나 메서드 하나의 논리 이메일 형식 검사 메서드가 틀린 주소를 걸러 내나 단위 테스트
중간 범위 테스트 부분 몇 개 사이의 주고받기 회원 저장 클래스가 데이터베이스에 회원을 넣고 다시 꺼내 오나 통합 테스트
넓은 범위 테스트 여러 부분이 이어진 흐름 전체 가입 화면에서 시작해 환영 메일 발송까지 끝나나 종단 간 테스트 · 시스템 테스트

표에서 볼 것은 가운데 줄이 확인하는 대상입니다. 한 부분의 계산도 아니고 흐름 전체도 아닙니다. 부분 몇 개가 만나는 경계입니다. 이 문서의 예에서는 회원 저장 클래스와 데이터베이스가 만나는 경계 하나입니다.

가짜가 모르는 것

좁은 범위 테스트는 검사하는 부분이 부르는 상대를 가짜로 바꿔 끼웁니다. 이유는 둘입니다. 진짜 데이터베이스를 부르면 느립니다. 결과도 흔들립니다. 이렇게 진짜 대신 끼우는 가짜를 테스트 더블이라고 합니다.

가짜는 테스트를 쓴 사람이 짐작한 대로만 답합니다. 회원 저장 클래스의 가짜 데이터베이스는 보통 해시맵 하나로 만듭니다. 넣으라면 넣습니다. 꺼내라면 꺼냅니다.

진짜 데이터베이스는 그보다 많은 것을 압니다. 이메일 칸에 고유 제약이 걸려 있다고 합시다. 고유 제약은 같은 값이 두 번 들어오면 저장을 거절하는 규칙입니다. 같은 이메일로 두 사람이 가입하는 일을 막으려고 겁니다.

가짜 해시맵은 이 규칙을 모릅니다. 그래서 「같은 이메일로 두 번 가입하면 거절된다」를 좁은 범위 테스트로는 확인할 수 없습니다. 중간 범위 테스트는 회원 저장 클래스를 진짜 데이터베이스에 붙입니다. 그러면 데이터베이스가 실제로 거절하는지 볼 수 있습니다.

테스트 한 벌

아래는 그런 테스트를 자바로 줄여 쓴 것입니다. repo 는 회원 저장 클래스입니다. 시험용 데이터베이스에 붙어 있습니다. 각 줄이 내는 값은 줄 오른쪽 주석에 적었습니다.

Java
repo.save(new Member("[email protected]"));
Member m = repo.findByEmail("[email protected]");
m.email();       // "[email protected]"
repo.count();    // 1
repo.save(new Member("[email protected]")); // 예외

첫 두 줄은 회원을 넣고 같은 이메일로 다시 꺼냅니다. 꺼낸 회원의 이메일은 넣은 값과 같습니다. 저장된 수도 하나입니다. 넣기와 꺼내기가 맞물렸다는 뜻입니다.

마지막 줄은 같은 이메일을 한 번 더 넣습니다. 데이터베이스의 고유 제약이 이 저장을 거절하므로 예외가 납니다. 가짜 해시맵을 붙였다면 이 줄은 조용히 지나갔을 겁니다.

무엇을 진짜로 두나

중간 범위 테스트는 확인할 경계의 양쪽만 진짜로 둡니다. 그 바깥은 여전히 가짜로 막습니다. 아래 그림은 회원 가입 테스트에서 무엇이 진짜이고 무엇이 가짜인지 보입니다.

flowchart TD
    T["테스트 코드"] --> R
    subgraph 진짜
        R["회원 저장 클래스"] --> D["시험용 데이터베이스"]
    end
    subgraph 가짜
        M["메일 발송 서비스"]
    end
    R -.-> M

테스트 코드는 회원 저장 클래스만 부릅니다. 회원 저장 클래스와 시험용 데이터베이스 사이가 이번에 확인할 경계입니다.

가입이 끝나면 환영 메일을 보내는 메일 발송 서비스도 있습니다. 이 서비스는 이번 경계 밖이라 가짜로 둡니다. 그림의 점선은 이렇게 가짜로 막아 둔 호출입니다. 메일이 실제로 나가는지는 넓은 범위 테스트가 볼 일입니다.

시험용 데이터베이스는 운영 데이터베이스와 따로 띄웁니다. 운영 데이터베이스를 쓰면 진짜 회원 데이터가 테스트에 섞이기 때문입니다.

컨테이너는 프로그램을 다른 프로그램과 떨어진 실행 환경에 담아 띄우는 방식입니다. 테스트를 돌릴 때마다 컨테이너로 새 데이터베이스를 띄우는 방법이 흔합니다.

메모리에서만 도는 데이터베이스로 대신하기도 합니다. 이것을 인메모리 데이터베이스라고 합니다. 인메모리 데이터베이스는 빨리 뜹니다. 대신 운영에서 쓰는 데이터베이스와 문법이나 제약이 조금씩 다를 수 있습니다. 그 차이에서 생기는 고장은 이 테스트가 못 잡습니다.

좁은 범위 · 넓은 범위와 가르는 선

확인하는 코드가 늘수록 테스트가 지나는 경계도 늘어납니다. 경계를 지난다는 것은 테스트의 요청이 진짜 부분 둘 사이를 실제로 건너간다는 뜻입니다. 그래서 세 범위는 경계를 몇 개 지나느냐로도 가를 수 있습니다.

좁은 범위 테스트는 경계를 하나도 안 지납니다. 상대 부분은 모두 가짜입니다.

중간 범위 테스트는 경계를 몇 개만 지납니다. 이 문서의 회원 가입 예는 하나입니다. 깨지면 그 경계 양쪽만 보면 됩니다. 원인을 찾기가 아직 쉽습니다.

넓은 범위 테스트는 경계 여러 개를 한 흐름으로 지납니다. 가입 화면에서 시작해 회원 저장을 거쳐 환영 메일 발송까지 끝나는지 봅니다. 깨지면 어느 경계에서 끊겼는지부터 찾아야 합니다.

크기와 범위는 다른 기준

범위 말고 테스트를 재는 잣대가 하나 더 있습니다. 테스트 크기입니다. 테스트 크기는 테스트가 어디서 도는지로 잽니다. 아래 표처럼 이것도 세 칸으로 나눕니다.

크기 도는 곳
작은 테스트 프로세스 하나
중간 테스트 머신 한 대
큰 테스트 여러 머신

표에서 보듯 크기는 도는 곳의 넓이입니다. 범위는 확인하는 코드의 양입니다. 둘은 서로 다른 축입니다.

가운데 칸인 중간 테스트는 이름이 중간 범위 테스트와 닮아 헷갈립니다. 중간 테스트는 크기로 잰 이름입니다. 중간 범위 테스트는 범위로 잰 이름입니다.

두 축은 자주 함께 움직입니다. 회원 저장 클래스와 데이터베이스를 한 머신 위의 두 프로세스로 띄운다고 합시다. 이 테스트는 범위로 중간입니다. 크기로도 중간 테스트입니다.

늘 그렇지는 않습니다. 인메모리 데이터베이스를 쓰면 데이터베이스가 테스트와 같은 프로세스 안에서 돕니다. 확인하는 범위는 여전히 중간입니다. 그런데 크기로는 작은 테스트가 됩니다.

치르는 값

중간 범위 테스트는 좁은 범위 테스트보다 느립니다. 데이터베이스를 띄우는 데 시간이 듭니다. 값을 쓸 때마다 디스크나 네트워크도 건넙니다. 좁은 범위 테스트 수십 개가 끝날 시간에 몇 개가 끝납니다.

데이터 정리도 일입니다. 테스트 여러 개가 같은 데이터베이스를 쓰면 앞 테스트가 넣은 회원이 남습니다. 뒤 테스트는 「저장된 수가 1」을 기대합니다. 그런데 2를 받습니다. 이렇게 테스트끼리 데이터로 서로를 흔드는 것을 테스트 데이터 오염이라고 합니다.

그래서 테스트마다 끝날 때 데이터를 지웁니다. 데이터베이스의 트랜잭션을 쓰는 방법도 흔합니다. 트랜잭션은 여러 작업을 한 묶음으로 처리하는 단위입니다. 묶음 안의 변경은 롤백으로 전부 없던 일로 되돌릴 수 있습니다.

테스트 하나를 트랜잭션 하나로 감쌉니다. 테스트가 끝나면 롤백합니다. 그러면 그 테스트가 넣은 회원은 다음 테스트가 시작하기 전에 사라집니다.

얼마나 두나

흔한 권장은 좁은 범위 테스트를 가장 많이, 넓은 범위 테스트를 가장 적게 두는 것입니다. 중간 범위 테스트는 그 사이입니다. 범위별 개수를 아래에서 위로 쌓으면 아래가 넓은 모양이 됩니다. 이 모양이 테스트 피라미드입니다.

구글은 대략 좁은 범위 80%, 중간 범위 15%, 넓은 범위 5%를 목표로 둡니다. 중간 범위 테스트는 수로는 적습니다. 그래도 경계마다 하나씩은 있어야 합니다.

이 칸이 비면 모래시계 모양이 됩니다. 좁은 범위와 넓은 범위 테스트는 많은데 중간 범위가 거의 없는 모양입니다. 경계 하나에서 난 고장이 넓은 범위 테스트까지 가서야 걸립니다. 그만큼 고장을 늦게 압니다. 원인도 흐름 전체에서 찾아야 합니다.

통합 테스트라는 이름

중간 범위 테스트를 흔히 통합 테스트라고 부릅니다. 그런데 통합 테스트는 쓰는 사람마다 가리키는 넓이가 다릅니다. 어떤 팀은 데이터베이스 하나를 붙인 테스트를 그렇게 부릅니다. 다른 팀은 서비스 여러 개를 다 띄운 테스트까지 그렇게 부릅니다.

범위라는 잣대는 이 흔들림을 줄이려고 씁니다. 「중간 범위」라고 하면 부분 몇 개 사이의 주고받기를 확인한다는 뜻으로 넓이가 정해집니다.

관련 항목

범위로 중간 범위 테스트와 나란히 서는 테스트

좁은 범위 테스트 · 넓은 범위 테스트 · 단위 테스트 · 통합 테스트 · 종단 간 테스트 · 시스템 테스트 · 기능 테스트 · 컴포넌트 테스트 · 계약 테스트

테스트를 범위 · 크기 · 개수로 가르는 분류

테스트 범위 · 테스트 크기 · 작은 테스트 · 중간 테스트 · 큰 테스트 · 테스트 피라미드 · 테스팅 트로피 · 테스트 수준

중간 범위 테스트가 경계 밖에 끼우는 가짜

테스트 더블 · 목 객체 · 스텁 · 페이크 · 인메모리 데이터베이스 · 목 서버

중간 범위 테스트가 확인하는 경계의 상대

데이터베이스 · 리포지토리 · 메시지 큐 · 캐싱 · 파일 시스템 · API

중간 범위 테스트에서 데이터를 다루는 수단

유니크 제약 · 트랜잭션 · 롤백 · 테스트 픽스처 · 테스트 데이터 오염 · 데이터베이스 마이그레이션

중간 범위 테스트가 도는 환경과 개발 단계

컨테이너 · 테스트 환경 · 지속적 통합 · 빌드 · 회귀 테스트 · 테스트 스위트

중간 범위 테스트에서 자주 나는 문제

불안정한 테스트 · 테스트 순서 의존성 · 깨지기 쉬운 테스트 · 타임아웃

중간 범위 테스트가 속하는 상위 분류

QA와 테스트 · 소프트웨어 테스트 · 자동 테스트 · 테스트 자동화

다른 이름: medium-scoped test · medium-scope test · medium-scoped testing · medium-scoped · 중간 범위의 테스트