데이터베이스 로직 테스트: 무엇을 테스트하고, 무엇을 건너뛰며, 왜 중요한가
Source: Dev.to
위에 제공된 소스 링크 외에 번역할 텍스트가 포함되어 있지 않습니다. 번역을 원하는 본문을 제공해 주시면 한국어로 번역해 드리겠습니다.
“데이터베이스 로직”이 실제 의미하는 바
개발자들이 데이터베이스 로직이라고 말할 때, 보통 단순 CRUD 작업 이상의 것을 의미합니다. 실제로는 다음을 포함합니다:
- 모델 수준 규칙 (계산된 필드, 상태 전이)
- 데이터베이스가 강제하는 제약조건 (고유 인덱스, 외래 키)
- 영속성에 의해 트리거되는 부수 효과 (이벤트, 옵저버, 작업)
- 마이그레이션: 스키마를 안전하게 진화시킴
- 비즈니스 가정을 인코딩하는 쿼리
데이터베이스 로직 테스트는 데이터베이스 엔진 자체를 테스트하는 것이 아닙니다. 실제 데이터가 관여할 때 애플리케이션이 올바르게 동작하는지를 검증하는 것입니다.
테스트 유형 구분
가장 흔한 실수 중 하나는 단위 테스트로 모든 것을 테스트하려는 것입니다. 순수 단위 테스트는 훌륭하지만 로직이 데이터베이스에 의존할 때는 한계가 있습니다.
데이터베이스 관련 테스트를 세 가지 카테고리로 나누는 것을 권장합니다:
- 빠른 모델 및 쿼리 테스트 – SQLite 인메모리 또는 전용 테스트 데이터베이스.
- 통합 테스트 – 관계와 제약 조건을 위해.
- 마이그레이션 테스트 – 완벽함보다 안전성에 초점을 맞춥니다.
모든 수준에서 모든 것을 테스트할 필요는 없습니다; 현실적으로 깨질 수 있는 부분만 테스트하면 됩니다. 안정적인 테스트 환경이 테스트 코드 자체보다 더 중요합니다.
Laravel의 기본 테스트 데이터베이스
DB_CONNECTION=sqlite
DB_DATABASE=:memory:
이는 빠른 피드백과 깨끗한 격리를 제공합니다. 그러나 한 가지 중요한 제한 사항을 인지하십시오: SQLite는 MySQL/PostgreSQL과 다르게 동작합니다, 특히 외래 키와 JSON 컬럼에서.
프로덕션 로직이 데이터베이스 특정 동작에 크게 의존한다면 Docker나 CI를 사용해 동일한 엔진으로 테스트를 실행하는 것을 고려하세요.
핵심 규칙: 테스트는 CI와 프로덕션에서 동일한 이유로 실패해야 합니다.
1️⃣ 고유성 강제
마이그레이션
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('email')->unique();
$table->timestamps();
});
테스트 (데이터베이스에 초점, 검증이 아니라)
public function test_user_email_must_be_unique()
{
User::factory()->create([
'email' => 'test@example.com',
]);
$this->expectException(QueryException::class);
User::factory()->create([
'email' => 'test@example.com',
]);
}
왜 중요한가: 이 테스트는 확고한 보장을 주장합니다—데이터베이스는 중복 이메일을 절대 허용하지 않습니다—검증이 어떻게 구현되든 관계없이. 이러한 테스트는 비용이 적고 빠르며 리팩터링 중에 매우 가치가 있습니다.
2️⃣ 관계 및 캐스케이드 규칙
마이그레이션
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')
->constrained()
->cascadeOnDelete();
});
테스트 (동작 중심)
public function test_orders_are_deleted_when_user_is_deleted()
{
$user = User::factory()->create();
$order = Order::factory()->create(['user_id' => $user->id]);
$user->delete();
$this->assertDatabaseMissing('orders', [
'id' => $order->id,
]);
}
왜 중요한가: 외래 키나 캐스케이드 규칙에 대한 실수로 인한 변경을 방지해 줍니다—이는 사람들이 생각보다 더 자주 발생하는 일입니다.
3️⃣ 모킹 안티패턴 피하기
일반적인 안티패턴은 Eloquent 모델이나 리포지토리를 모킹하여 데이터베이스 로직을 테스트하는 것입니다. 이는 테스트는 통과하지만 실제 운영 환경에서는 오류가 발생하는 경우가 많습니다.
로직이 다음에 의존한다면:
- 데이터베이스 제약조건
- 트랜잭션 동작
- 실제 영속된 상태
…그렇다면 모킹하지 마세요.
예시: 트랜잭션 작업 테스트
DB::transaction(function () {
$order->markAsPaid();
$invoice->generate();
});
올바른 테스트 (최종 상태 검증)
public function test_order_is_paid_and_invoice_is_created()
{
$order = Order::factory()->create();
$service = new OrderPaymentService();
$service->pay($order);
$this->assertDatabaseHas('orders', [
'id' => $order->id,
'status' => 'paid',
]);
$this->assertDatabaseHas('invoices', [
'order_id' => $order->id,
]);
}
이러한 테스트는 리팩터링 시 모킹보다 훨씬 더 잘 견딥니다.
Source: …
4️⃣ 마이그레이션 테스트 – 위험을 테스트하고, 모든 컬럼을 테스트하지 않음
마이그레이션 테스트는 종종 생략되거나 비현실적으로 수행됩니다. 모든 컬럼을 테스트할 필요는 없으며, 위험을 테스트해야 합니다.
마이그레이션 테스트에 적합한 후보
- 데이터 변환
- 컬럼 이름 변경
- 백필된 값
- 제약 조건 삭제 또는 강화
예시: 기본값이 있는 non‑null 컬럼 추가
마이그레이션
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_active')->default(true);
});
테스트
public function test_existing_users_are_active_after_migration()
{
$user = User::factory()->create([
'is_active' => null,
]);
$this->artisan('migrate');
$user->refresh();
$this->assertTrue($user->is_active);
}
왜 중요한가: 이는 실제 프로덕션 이슈—잘못된 기존 데이터로 인한 배포 실패—를 방지합니다.
5️⃣ 데이터베이스 테스트 속도 향상
데이터베이스 테스트는 느리다는 평판이 있습니다. 대부분의 프로젝트에서 이는 데이터베이스 때문이 아니라 테스트 설계 때문입니다.
실용적인 규칙
- 최소 기본값을 가진 팩토리를 사용하세요.
- 불필요한 시딩을 피하세요.
- 가능하면 트랜잭션을 사용해 데이터베이스를 리셋하세요.
- 같은 제약 조건을 열 개의 서로 다른 테스트에서 검증하지 마세요.
속도는 단순히 편리함을 위한 것이 아니라; 느린 테스트는 무시됩니다.
TL;DR
| 테스트 유형 | 목표 | 일반적인 도구 |
|---|---|---|
| 빠른 모델/쿼리 | 기본 쿼리 및 스코프 검증 | SQLite 인‑메모리 |
| 통합 | 관계, 제약조건, 연쇄 규칙 확인 | 실제 DB (MySQL/Postgres) 또는 Docker |
| 마이그레이션 | 위험한 스키마 변경이 기존 데이터를 깨뜨리지 않도록 보장 | Artisan 마이그레이션 + 어설션 |
이렇게 데이터베이스 테스트 스위트를 구성하면 빠른 피드백, 높은 신뢰성, 그리고 유지보수 가능한 코드를 얻을 수 있습니다—Laravel의 강력한 Eloquent ORM을 사용할 때 정확히 필요한 것입니다. 즐거운 테스트 되세요!
테스트 철학: 테스트하지 말아야 할 것
건너뛴 테스트는 테스트가 없는 것보다 더 나쁩니다.
피해야 할 테스트 항목
- Laravel의 내부 Eloquent 동작
- 데이터베이스 엔진 구현 세부 사항
- 프레임워크 제공 마이그레이션
- 논리 없이 단순한 getter/setter
집중해야 할 것
- 비즈니스 보장에 초점을 맞추고, 기계적인 구현에 집중하지 마세요.
데이터베이스 로직과 마이그레이션 테스트는 100 % 커버리지를 달성하는 것이 아니라 두려움을 줄이는 것입니다:
- 리팩토링에 대한 두려움
- 배포에 대한 두려움
- 오래된 코드를 건드리는 것에 대한 두려움
잘 작성된 데이터베이스 테스트는 실행 가능한 문서 역할을 합니다. 이는 미래의 당신(또는 팀원)에게 코드베이스가 진화하더라도 절대 깨지면 안 되는 것이 무엇인지 알려줍니다.
요약
데이터베이스를 외부 의존성이 아니라 협업 파트너로 테스트하세요.
이러한 사고방식을 채택하면 테스트 스위트와 시스템에 대한 자신감이 크게 향상됩니다.