Software Design

좋은 단위 테스트의 조건, FIRST 원칙 5가지 총정리

테스트 코드를 잔뜩 짜놨는데도 정작 버그는 못 잡고, 기능 하나 고칠 때마다 테스트가 우수수 깨진 경험 있으신가요?

이석우iOS Developer5분 읽기
좋은 단위 테스트의 조건, FIRST 원칙 5가지 총정리 대표 이미지

테스트 코드를 잔뜩 짜놨는데도 정작 버그는 못 잡고, 기능 하나 고칠 때마다 테스트가 우수수 깨진 경험 있으신가요?

결론부터 말씀드리면, 좋은 단위 테스트는 개수나 커버리지가 아니라 ’FIRST 원칙’을 지켰는지로 판가름 납니다. FIRST는 Fast(빠르게), Isolated(독립적으로), Repeatable(반복 가능하게), Self-validating(스스로 검증), Timely(적시에)의 앞글자예요.

이 다섯 가지만 기억하면 테스트가 짐이 아니라 든든한 안전망이 됩니다. 오늘은 하나씩, 제 경험을 곁들여 풀어볼게요.

좋은 단위 테스트의 조건, FIRST 다섯 글자만 기억하면 됩니다
좋은 단위 테스트의 조건, FIRST 다섯 글자만 기억하면 됩니다

FIRST 원칙, 한눈에 정리하면?

FIRST는 로버트 마틴(엉클 밥)이 저서 ’클린 코드’에서 소개하면서 널리 퍼진 원칙입니다.

글자마다 좋은 단위 테스트의 조건 하나를 담고 있어요.

글자 의미 핵심
F Fast 수백 개를 몇 초 안에
I Isolated 테스트끼리 서로 간섭 없이
R Repeatable 언제 어디서 돌려도 같은 결과
S Self-validating 통과/실패를 자동 판정
T Timely 적절한 시점에 작성

하나씩 뜯어보겠습니다.


Fast, 왜 이렇게 빨라야 할까요?

단위 테스트는 빨라야 합니다. 정말요.

느리면 개발자가 안 돌리거든요. 한 번 돌리는 데 3분씩 걸리는 테스트 묶음은 어느 순간부터 커밋 전에만 겨우 돌리게 됩니다.

테스트는 코드를 고칠 때마다 부담 없이 수백 번 돌릴 수 있어야 제 몫을 합니다.

느려지는 가장 흔한 원인은 실제 DB, 네트워크, 파일 시스템 접근이에요. 이런 외부 의존성은 테스트 대역(Mock, Stub)으로 대체하는 게 정석입니다.

한 개당 몇 밀리초, 전체 수백 개를 몇 초 안에 끝내는 걸 목표로 잡으면 좋습니다.

테스트가 전부 초록불일 때의 이 안도감, 아시죠?
테스트가 전부 초록불일 때의 이 안도감, 아시죠?

Isolated, 테스트가 서로 간섭하면 안 되는 이유

각 테스트는 독립적이어야 합니다. 다른 테스트의 결과에 의존하면 안 돼요.

예를 들어 A 테스트가 만들어 둔 데이터를 B 테스트가 가져다 쓰면, A가 실패하는 순간 B도 덩달아 무너집니다. 실행 순서만 바뀌어도 결과가 달라지고요.

그래서 각 테스트는 자기가 쓸 데이터를 스스로 준비하고, 끝나면 깔끔하게 치워야 합니다.

아래는 매 테스트 전에 상태를 초기화하는 흔한 패턴이에요.

struct CalculatorTests {
    let calculator: Calculator

    init() {
        // 매 테스트마다 suite 인스턴스가 새로 만들어짐 → 상태 공유 차단
        calculator = Calculator()
    }
}

이렇게 하면 어떤 테스트가 먼저 돌든 서로에게 영향을 주지 않습니다.

Xcode에서 @Test와 init()으로 작성한 Swift Testing 테스트 스위트와 통과 결과 화면
init() 덕분에 매 테스트가 새 인스턴스로 시작해 서로 독립됩니다

Repeatable & Self-validating, 결과가 흔들리면 안 됩니다

R, 반복 가능성부터 볼게요. 테스트는 몇 번을 돌려도, 어떤 환경에서 돌려도 같은 결과가 나와야 합니다.

오늘은 통과했는데 내일은 실패하는 테스트를 ’깨지기 쉬운(flaky) 테스트’라고 부르는데요. 신뢰를 무너뜨리는 주범입니다.

현재 시각이나 랜덤 값에 의존하면 이런 문제가 생기기 쉬워요. 시간을 다뤄야 한다면 고정된 값을 주입해서 통제하는 게 안전합니다.

S, 자기 검증도 중요합니다. 테스트는 통과인지 실패인지 스스로 판단해야 해요.

콘솔에 값을 찍어놓고 사람이 눈으로 확인하는 건 테스트가 아닙니다. #expect 같은 단언문으로 결과를 자동 판정하게 만들어야 합니다.

좋은 테스트는 초록불 아니면 빨간불, 둘 중 하나만 보여줍니다. ‘이게 맞나?’ 하고 사람이 고민하게 만들면 이미 실패한 테스트예요.


Timely, 테스트는 언제 짜는 게 좋을까요?

마지막 T는 적시성입니다. 테스트를 적절한 시점에 작성하라는 뜻이에요.

TDD(Test-Driven Development, 테스트 주도 개발)에서는 프로덕션 코드보다 테스트를 먼저 씁니다. 꼭 그 방식을 따르지 않더라도, 기능 구현과 가까운 시점에 함께 작성하는 게 핵심입니다.

기능을 다 만들고 몇 주 뒤에 테스트를 몰아서 짜려고 하면, 그땐 이미 테스트하기 어려운 구조로 굳어 있는 경우가 많거든요.

테스트를 나중 일로 미루지 않는 것, 그게 Timely의 핵심입니다.


마무리하며

FIRST는 외우기 위한 규칙이 아니라, 테스트가 답답하게 느껴질 때 어디가 어긋났는지 짚어주는 체크리스트에 가깝습니다.

느린가? 서로 얽혀 있나? 결과가 흔들리나? 사람 손을 타나? 너무 늦게 짜고 있나? 이 다섯 개만 점검해도 테스트의 질이 확 올라갑니다.

오늘부터 테스트 하나 짤 때 FIRST를 슬쩍 떠올려 보세요. 분명 든든한 동료가 되어줄 거예요.

이어서 읽기