테스트 도구 실전 · 무엇을 테스트하나 · 이론
커버리지는 목표가 아니라 지도다
한 줄 요약
커버리지는 어디를 안 봤는지 알려 주는 지도다. 100%를 목표로 삼는 순간 지도가 아니라 채워야 할 칸이 되고, 그때부터 아무것도 검증하지 않는 테스트가 늘어난다.
왜 이게 필요했나
이런 테스트를 본 적이 있을 것이다.
def test_create_user(): u = create_user("kim") assert u is not None이 테스트는 create_user 의 모든 줄을 실행하지만 아무것도 확인하지 않는다. 커버리지는 100%가 되고, 이름이 저장되지 않아도 통과한다.
반대로 진짜 위험한 곳은 대개 커버리지 밖에 있다. 오류 처리 분기, 경계값, 동시 실행. 그런 곳은 테스트를 쓰기 귀찮아서 안 쓴 곳이고, 정확히 그래서 버그가 산다.
무엇을 먼저 테스트하나
우선순위는 틀렸을 때의 비용으로 정한다.
| 우선 | 무엇 | 왜 |
|---|---|---|
| 1 | 돈·권한·데이터 삭제 | 틀리면 되돌릴 수 없다 |
| 2 | 분기가 많은 순수 함수 | 싸게 많이 검증된다 |
| 3 | 경계값과 오류 경로 | 사람이 잘 안 짜 보는 곳 |
| 4 | 통합 경로 하나 | 조각이 붙는지 |
| 5 | 화면 | 가장 비싸고 가장 자주 깨진다 |
이 순서가 테스트 피라미드가 말하는 것이다 — 아래(단위)를 넓게, 위(E2E)를 얇게.
커버리지를 읽는 법
숫자가 아니라 빠진 줄을 본다.
pytest --cov=mymod --cov-report=term-missingName Stmts Miss Cover Missingmymod.py 42 6 86% 17-19, 28, 51-52Missing 이 답이다. 17~19가 오류 처리라면 그건 채워야 할 곳이고, 51~52가 로깅이라면 안 채워도 된다. 판단은 사람이 한다.
분기 커버리지를 켜면 더 정확해진다.
pytest --cov=mymod --cov-branchif x: 를 참으로만 지나가면 줄 커버리지는 100%지만 분기 커버리지는 50%다.
흔한 착각
"테스트가 통과하면 맞다" — 테스트는 버그의 존재를 보일 뿐 없음을 증명하지 못한다. 통과는 "내가 생각한 경우에는 맞다" 이상을 뜻하지 않는다.
"느린 테스트도 테스트다" — 느리면 안 돌린다. 안 돌리는 테스트는 없는 테스트보다 나쁘다(있다고 믿게 만든다). 단위 테스트 전체가 몇 초 안에 끝나야 한다.
실무에서 진짜 중요한 것
테스트가 실패했을 때 원인이 바로 보이는가가 테스트의 값어치다.
# 나쁨 — 왜 틀렸는지 모른다assert result == expected# 좋음 — 무엇이 다른지 보인다assert result.status == 200, f"응답: {result.status} {result.body[:200]}"pytest 는 assert a == b 의 양쪽을 자동으로 보여 준다. 그래서 조건을 잘게 쪼개는 것이 메시지를 길게 쓰는 것보다 낫다.