테스트 스위트
고친 사람 github-actions[bot]
테스트 스위트는 여러 테스트를 한데 묶어 명령 한 번으로 모두 돌려 줍니다. 결과도 통과한 수와 실패한 수로 한꺼번에 알려 줍니다. 테스트가 수백 개로 늘어도 무엇을 돌렸는지 사람이 기억할 필요가 없습니다. 코드를 올릴 때마다 서버가 자동으로 돌리는 것도 대개 이 묶음입니다.
쉽고 빠른 이해
무슨 일을 하나 — 테스트 여러 개를 묶음 하나로 만들어 명령 한 번에 전부 돌립니다. 주문 서비스의 테스트 마흔다섯 개를 한 번에 돌리고 「44개 통과, 1개 실패」를 받는 식입니다.
왜 하나 — 무엇을 돌릴지 사람이 고르면 고친 곳 주변만 돌리기 쉽습니다. 멀리 떨어진 기능이 깨진 것은 그렇게 놓칩니다. 묶어 두면 누가 돌리든 전부 돕니다.
어떻게 도나
- 테스트를 기능별 묶음으로 모읍니다. 그 묶음들을 다시 큰 묶음으로 모읍니다
- 테스트를 돌리는 도구가 묶음 안의 테스트를 하나씩 부르고 결과를 적습니다
- 하나라도 실패하면 실패 신호를 냅니다. 자동으로 도는 서버는 그 신호를 보고 변경을 멈춥니다
대가 — 테스트가 쌓일수록 한 번 도는 시간이 길어집니다. 그래서 금방 끝나는 것과 오래 걸리는 것을 다른 묶음으로 나눠 따로 돌립니다. 결과가 들쭉날쭉한 테스트가 섞이면 묶음 전체의 결과를 믿기 어려워집니다.
상세
이 절은 주문 서비스 하나를 예로 테스트 스위트를 봅니다. 이 서비스에는 가격 계산, 재고 차감, 결제 연동을 검사하는 테스트가 모두 마흔다섯 개 있다고 하겠습니다.
비행기 조종사는 이륙하기 전에 점검표를 끝까지 확인합니다. 항목 하나하나는 연료가 찼는지, 문이 닫혔는지 같은 짧은 확인입니다. 한 장에 모아 두었기에 빠뜨리는 항목이 없습니다. 한 줄이라도 걸리면 비행기는 뜨지 않습니다.
정해 둔 입력을 넣고 나온 결과가 기대한 값과 같은지 확인하는 검사 하나를 테스트 케이스라고 부릅니다. 「2만 원어치를 사면 1만 8천 원이 나와야 한다」가 테스트 케이스 하나입니다. 이 글에서 「테스트」라고만 하면 테스트 케이스 하나를 가리킵니다.
테스트 스위트는 이런 테스트 여러 개를 한 단위로 묶은 것입니다. 묶음 전체를 한 번에 돌립니다. 결과도 묶음 단위로 받습니다. 주문 서비스의 테스트 마흔다섯 개를 모두 담은 묶음이 이 서비스의 테스트 스위트입니다.
묶음이 없으면 무엇을 돌릴지를 사람이 고릅니다. 가격 계산을 고친 사람은 가격 계산 테스트만 돌리기 쉽습니다. 그 변경이 결제 연동을 깨뜨렸다면 아무도 모른 채 넘어갑니다.
스위트로 묶어 두면 「전부 돌렸고 전부 통과했다」를 한 번에 확인합니다. 테스트를 새로 쓰면 스위트에 넣기만 하면 됩니다. 대개 테스트 파일을 정해진 폴더에 두기만 하면 스위트에 들어갑니다. 그다음부터는 누가 돌리든 그 테스트도 함께 돕니다.
한 번 통과한 테스트는 지우지 않고 스위트에 남겨 둡니다. 이미 되던 기능이 다시 안 되는 일을 회귀라고 부릅니다. 쌓인 테스트를 전부 다시 돌려 회귀를 찾는 일이 회귀 테스트입니다. 스위트는 그 일을 명령 한 번으로 줄여 줍니다.
스위트 안에 든 스위트
이 소절은 스위트가 어떻게 겹겹이 짜이는지를 봅니다. 주문 서비스의 테스트 마흔다섯 개를 기능별로 나눠 봅니다.
스위트는 테스트뿐 아니라 다른 스위트도 담을 수 있습니다. 가격 계산 테스트 스무 개를 가격 계산 스위트로 묶습니다. 재고와 결제도 같은 식으로 묶습니다. 그리고 이 셋을 다시 주문 서비스 스위트 하나로 묶습니다.
flowchart TD
S["주문 서비스 스위트"]
S --> P["가격 계산 스위트"]
S --> I["재고 차감 스위트"]
S --> Y["결제 연동 스위트"]
P --> P1["2만 원 이상이면 2천 원 깎는다"]
P --> P2["2만 원 미만이면 깎지 않는다"]
P --> P3["나머지 테스트 18개"]
I --> I1["테스트 15개"]
Y --> Y1["테스트 10개"]
그림의 맨 아래 줄이 테스트입니다. 그 위의 네모는 전부 스위트입니다. 어느 계층의 스위트든 따로 골라 돌릴 수 있습니다. 가격 계산 코드를 고치는 동안에는 가격 계산 스위트만 돌려 몇 초 만에 확인합니다. 코드를 올리기 전에는 맨 위 스위트를 돌려 전부 확인합니다.
테스트를 돌리는 도구는 대개 코드 구조를 따라 이 계층을 저절로 만듭니다. 테스트 파일 하나나 테스트 클래스 하나가 스위트 하나가 됩니다. 그 파일이 든 폴더는 한 단계 위의 스위트가 됩니다.
한 번 돌릴 때 벌어지는 일
이 소절은 스위트를 한 번 돌리면 무엇이 어떤 순서로 일어나는지를 봅니다. 돌리는 일을 맡는 프로그램부터 풉니다.
스위트를 받아 안에 든 테스트를 하나씩 부르는 프로그램을 테스트 러너라고 합니다. 앞에서 「테스트를 돌리는 도구」라 부른 것이 이 러너입니다. 러너는 테스트마다 결과를 적어 두었다가 끝에 모아서 보여 줍니다. 스위트가 무엇을 돌릴지 적은 목록이라면, 러너는 그 목록을 돌리는 프로그램입니다.
러너는 대개 다섯 단계로 스위트를 돌립니다.
- 스위트 안의 테스트를 모두 찾아 목록을 만듭니다
- 스위트 전체에 필요한 준비를 한 번 합니다
- 테스트를 하나씩 부르고 통과인지 실패인지 적습니다
- 준비했던 것을 치웁니다
- 통과와 실패 수를 모아 보여 줍니다. 그리고 종료 코드를 돌려줍니다
테스트 하나가 실패해도 러너는 대개 멈추지 않고 나머지를 끝까지 돌립니다. 실패가 몇 개이고 어느 테스트인지를 한 번에 알아야 고칠 순서를 정할 수 있기 때문입니다. 결과 보고에는 실패한 테스트의 이름이 뜹니다. 기대한 값과 나온 값도 함께 뜹니다.
마지막 단계의 종료 코드는 프로그램이 끝나면서 돌려주는 숫자입니다. 0 이면 성공, 그 밖의 숫자면 실패로 읽는 것이 관례입니다. 러너는 테스트가 하나라도 실패하면 0 이 아닌 숫자를 돌려줍니다. 사람이 결과 화면을 읽지 않아도 다른 프로그램이 이 숫자 하나로 통과 여부를 압니다.
스위트 전체가 나눠 쓰는 준비
이 소절은 둘째 단계의 준비를 봅니다. 테스트용 데이터베이스를 예로 듭니다.
테스트가 돌기 전에 갖춰 두어야 하는 값이나 환경을 테스트 픽스처라고 합니다. 주문 테스트라면 상품 몇 개가 들어 있는 테스트용 데이터베이스가 픽스처입니다. 픽스처가 없으면 테스트마다 같은 준비 코드를 되풀이해 적어야 합니다.
픽스처는 테스트마다 새로 만들 수 있습니다. 스위트 전체가 하나를 나눠 쓸 수도 있습니다. 데이터베이스를 띄우는 데 몇 초가 걸린다면 테스트 마흔다섯 개마다 띄우는 것은 느립니다. 스위트 시작에 한 번 띄우고 끝에 한 번 내리면 그 시간을 한 번만 씁니다.
나눠 쓰는 대가는 앞 테스트가 남긴 데이터입니다. 그 테스트가 데이터베이스에 넣은 주문이 뒤 테스트의 결과를 바꿀 수 있습니다. 나눠 쓸 때는 테스트마다 끝에 자기가 남긴 데이터를 지우게 합니다. 테스트마다 서로 다른 데이터만 쓰게 하는 방법도 있습니다.
테스트끼리 기대지 않는 성질
스위트 안의 테스트는 순서가 바뀌어도 결과가 같아야 합니다. 이 소절은 주문을 만드는 테스트와 취소하는 테스트 한 쌍으로 이 성질을 봅니다.
러너가 테스트를 부르는 순서는 늘 같지 않습니다. 파일 이름 순으로 부르는 러너가 있습니다. 일부러 순서를 섞어 부르는 러너도 있습니다. 여러 테스트를 동시에 돌려 시간을 줄이는 병렬 실행을 쓰면 순서라는 것이 아예 없어집니다.
주문을 만드는 테스트가 넣은 데이터를 주문을 취소하는 테스트가 꺼내 쓴다고 해 봅시다. 두 테스트는 이 순서로 돌 때만 통과합니다. 취소 테스트 하나만 골라 돌리거나 순서가 뒤집히면 실패합니다. 이렇게 다른 테스트가 먼저 돌았는지에 결과가 달린 상태를 테스트 순서 의존성이라고 합니다.
이를 막으려면 테스트마다 자기가 쓸 데이터를 스스로 준비합니다. 취소 테스트는 취소할 주문을 스스로 만든 뒤에 취소합니다. 그러면 어느 테스트를 혼자 돌려도, 어느 순서로 돌려도 결과가 같습니다.
속도로 가르는 스위트
이 소절은 스위트가 커졌을 때 어떻게 나누는지를 봅니다. 기준은 대개 한 번 도는 데 걸리는 시간입니다.
테스트는 종류마다 빠르기가 다릅니다. 흔히 쓰는 세 종류를 금방 끝나는 것부터 놓으면 이렇습니다.
- 단위 테스트는 함수 하나를 떼어 검사합니다. 금방 끝납니다
- 통합 테스트는 데이터베이스나 다른 서비스를 진짜로 붙여 검사합니다. 단위 테스트보다 훨씬 느립니다
- 종단 간 테스트는 브라우저를 띄워 사용자처럼 화면을 눌러 봅니다. 셋 중 가장 느립니다
모두를 한 스위트에 넣으면 가장 오래 걸리는 테스트가 전체 시간을 정합니다. 코드를 올릴 때마다 수십 분을 기다려야 한다면 개발자는 스위트를 덜 돌리게 됩니다. 그래서 금방 끝나는 것과 오래 걸리는 것을 다른 스위트로 나눕니다. 돌리는 때도 달리합니다.
배포한 직후에 핵심 기능만 빠르게 훑는 스모크 테스트도 흔히 따로 스위트를 둡니다. 아래 표는 이렇게 나눈 세 스위트를 나란히 놓습니다. 이름은 돌리는 때나 담는 테스트를 따서 붙였습니다.
| 스위트 | 담는 테스트 | 돌리는 때 |
|---|---|---|
| 커밋 스위트 | 단위 테스트 | 코드를 올릴 때마다 |
| 통합 스위트 | 통합 테스트 · 종단 간 테스트 | 커밋 스위트를 통과한 뒤 |
| 스모크 스위트 | 핵심 흐름 몇 개 | 배포한 직후 |
표에서 볼 것은 셋째 칸입니다. 스위트마다 돌리는 때가 다릅니다. 빨리 끝나는 스위트일수록 자주 돕니다.
지속적 통합은 누가 코드를 올릴 때마다 서버가 빌드하고 테스트를 돌려 주는 방식입니다. 서버는 커밋 스위트부터 돌립니다. 그다음 러너의 종료 코드를 봅니다. 0 이 아니면 그 변경은 뒤 단계로 넘어가지 않습니다.
변경이 스위트를 차례로 통과하며 배포까지 가는 길을 배포 파이프라인이라고 부릅니다. 커밋 스위트를 앞에 두는 까닭은 싼 검사로 먼저 거르려는 것입니다. 몇 분 만에 걸러질 실수를 수십 분짜리 스위트가 잡게 둘 이유가 없습니다.
flowchart TD
A["코드를 올린다"] --> B["커밋 스위트"]
B -->|실패| X["변경을 멈추고 고친다"]
B -->|통과| C["통합 스위트"]
C -->|실패| X
C -->|통과| D["배포"]
D --> E["스모크 스위트"]
그림에서 실패 화살표는 커밋 스위트에서 나오든 통합 스위트에서 나오든 같은 곳으로 갑니다. 앞 스위트에서 걸릴수록 기다린 시간이 짧습니다. 스모크 스위트는 배포한 뒤에 돌므로, 실패하면 방금 한 배포를 되돌리거나 곧바로 고칩니다.
결과를 믿을 수 있는 스위트
이 소절은 스위트의 결과를 믿기 어렵게 만드는 것 둘을 봅니다. 들쭉날쭉한 테스트와, 통과가 말해 주지 않는 것입니다.
코드를 바꾸지 않아도 돌릴 때마다 통과했다 실패했다 하는 테스트가 있습니다. 이런 테스트를 불안정한 테스트라고 합니다. 현재 시각이나 네트워크에 기대는 테스트, 앞에서 본 순서 의존성이 흔한 원인입니다.
불안정한 테스트가 몇 개만 있어도 사람들은 스위트의 실패를 무시하기 시작합니다. 「또 그 테스트겠지」 하고 다시 돌리는 버릇이 생깁니다. 그러다 진짜 결함이 낸 실패도 같이 넘깁니다. 그래서 이런 테스트는 바로 고치거나, 고칠 때까지 스위트에서 따로 빼 둡니다.
스위트가 전부 통과해도 알 수 있는 것은 테스트가 확인한 것까지입니다. 테스트를 안 쓴 기능은 깨져도 스위트가 조용합니다. 테스트가 코드의 어디까지 지나갔는지는 테스트 커버리지로 잽니다.
표준을 지켰는지 재는 스위트
테스트 스위트라는 이름은 표준을 다루는 쪽에서도 씁니다. 이 소절은 그 쓰임을 짧게 봅니다.
프로토콜이나 파일 형식을 정한 표준이 있으면 여러 곳이 저마다 그 표준을 구현합니다. 각 구현이 표준을 제대로 따르는지 보려고 공용 테스트 묶음을 만들어 둡니다. 이 묶음도 테스트 스위트라고 부릅니다. 이렇게 하는 검사를 적합성 시험이라고 합니다.
묶음의 뜻은 같습니다. 테스트 여러 개를 한 단위로 묶어 한 번에 돌립니다. 결과도 한데 모읍니다.
다른 것은 검사 대상입니다. 한 팀이 자기 코드를 검사하는 스위트와 달리, 이 스위트는 서로 다른 곳이 만든 구현을 같은 잣대로 잽니다.
관련 항목
테스트 스위트를 이루는 구성 요소
테스트 케이스 · 단언 · 테스트 픽스처 · 테스트 클래스 · 테스트 메서드
테스트 스위트를 돌리는 도구
테스트 러너 · JUnit · pytest · Jest · xUnit
테스트 스위트에 담기는 테스트 종류
단위 테스트 · 통합 테스트 · 종단 간 테스트 · 인수 테스트 · 스모크 테스트 · 회귀 테스트 · 성능 테스트
테스트 스위트를 돌리는 개발 단계
지속적 통합 · 지속적 배포 · 지속적 전달 · 배포 파이프라인 · 커밋 빌드 · 빌드
테스트 스위트의 결과를 흐리는 문제
불안정한 테스트 · 테스트 순서 의존성 · 깨지기 쉬운 테스트 · 공유 픽스처
테스트 스위트의 빈 곳을 재는 지표
테스트 커버리지 · 분기 커버리지 · 구문 커버리지 · 뮤테이션 테스트
테스트 스위트를 빠르게 유지하는 방법
병렬 실행 · 테스트 샤딩 · 테스트 피라미드 · 테스트 더블
표준 쪽에서 테스트 스위트를 쓰는 검사
적합성 시험 · 상호운용성 · 상호운용성 시험 · 레퍼런스 구현
테스트 스위트가 속하는 상위 분류
다른 이름: test suite · 테스트 묶음 · 테스트 모음