테스트 코드는 단순한 검증 도구가 아니라 시스템의 행동을 설명하는 생동하는 문서입니다. 특히 Pest는 PHP에서 BDD 중심의 간결한 문법을 제공하므로, 명확하고 일관된 명명 방식은 팀 협업과 장기적인 코드 품질에 직접적인 영향을 미칩니다.
행동 중심 명명 전략
Pest의 it() 함수는 자연어 스타일로 테스트 의도를 표현하기에 적합합니다. 다음 구조를 권장합니다:
// 권장: 조건과 결과를 명시
it('redirects to dashboard after successful login')
->post('/login', ['email' => 'admin@test.dev', 'password' => 'pass123'])
->assertRedirect('/dashboard');
// 비권장: 추상적이거나 구현 세부사항 포함
test('testLoginSuccess') { /* ... */ }
동사 기반 동작 표현
테스트 이름은 반드시 동사로 시작해야 하며, 해당 테스트가 무엇을 검증하는지 한눈에 파악할 수 있어야 합니다:
- validates: 입력 유효성 검사 —
it('validates email format') - retrieves: 데이터 조회 —
it('retrieves active subscriptions') - blocks: 접근 차단 —
it('blocks unverified users from payment flow') - enqueues: 작업 큐 등록 —
it('enqueues notification on order confirmation') - serializes: 직렬화 동작 —
it('serializes product variant with pricing')
계층적 컨텍스트 구성
describe()를 사용해 논리적 계층을 형성하면, 실행 결과 출력에서도 구조가 명확해집니다:
describe('SubscriptionManager', function () {
describe('::activate()', function () {
it('activates subscription when trial period ends')
->given(new TrialEndedEvent())
->when(fn() => $this->manager->activate($subscription))
->then(fn($sub) => expect($sub->status)->toBe('active'));
describe('with overdue invoice', function () {
it('does not activate until payment is settled')
->given(new OverdueInvoiceDetectedEvent())
->when(fn() => $this->manager->activate($subscription))
->then(fn($sub) => expect($sub->status)->toBe('pending_payment'));
});
});
});
출력 예시:
SubscriptionManager > ::activate() > activates subscription when trial period ends
SubscriptionManager > ::activate() > with overdue invoice > does not activate until payment is settled
데이터 기반 테스트의 명명
파라미터화된 테스트에서는 각 케이스가 무엇을 검증하는지를 이름에 반영하세요:
it('formats currency correctly for {locale}', function ($locale, $amount, $expected) {
expect(formatCurrency($amount, $locale))->toBe($expected);
})->with([
'korean_won' => ['ko_KR', 100000, '₩100,000'],
'us_dollar' => ['en_US', 99.99, '$99.99'],
]);
상태 및 의도 표시 접두사
특정 상태나 개발 단계를 나타내는 접두사를 활용해 테스트의 의도를 즉각 전달할 수 있습니다:
⚠️ skips legacy endpoint migration test— 일시적으로 건너뜀🧪 verifies new rate-limiting algorithm— 실험적 기능🔍 confirms edge-case behavior in refund workflow— 디버깅 목적
비즈니스 용어 통합
기술 용어보다 도메인 언어를 우선 사용하면, 제품 및 QA 팀과의 소통 효율성이 향상됩니다:
describe('InventoryAllocator', function () {
it('reserves stock upon order placement')
->given(orderWithItems(['SKU-101' => 2]))
->when(fn() => allocate($order))
->then(fn() => expect(Stock::find('SKU-101')->reserved)->toBe(2));
it('releases reserved stock on order cancellation')
->given(reservedOrder())
->when(fn() => cancel($order))
->then(fn() => expect(Stock::find('SKU-101')->reserved)->toBe(0));
});
피해야 할 명명 패턴
| 문제 있는 이름 | 왜 문제인가 | 개선된 이름 |
|---|---|---|
testUserCreation | 행동과 조건 부재 | it('creates user account with verified email') |
testApiV2 | 버전 정보만으로 의미 불명 | describe('API v2 endpoints') + 구체적 it() 케이스 |
testHappyPath | 모호한 용어, 재현 불가능 | it('processes valid checkout request') |
자동화된 품질 보장
pest-plugin-test-naming을 설치해 정적 분석을 활성화하면, 다음과 같은 규칙을 강제할 수 있습니다:
- 최소 길이 12자 이상
- "it" 또는 "test"로 시작하며, "should", "can", "will" 등 모달 동사는 제외
- 숫자나 약어 포함 금지 (예:
testUppercase→it('converts string to uppercase'))
실무 체크리스트
- 이름을 읽고 "어떤 상황에서 어떤 결과가 발생해야 하는가?"를 바로 대답할 수 있나요?
- 클래스나 메서드 이름이 아닌, 사용자 관점의 행동에 초점을 두고 있나요?
describe()계층이 실제 서비스/도메인 구조와 일치하나요?- 파라미터 테스트의 경우, 각 케이스가 어떤 비즈니스 조건을 반영하는지 명시되었나요?
- 팀 내 모든 테스트가 동사 중심, 조건 명시, 중복 없이 작성되어 있나요?