2026년 Laravel 테스트: Pest 5, TIA, 모킹 및 기술 면접 질문
Pest 5, Test Impact Analysis, Mockery, 파사드 페이크, 아키텍처 테스트를 활용한 Laravel 테스트 모범 사례. 단위 테스트, 기능 테스트, 모킹 전략과 Laravel 개발자를 위한 일반적인 면접 질문을 다룹니다.

Laravel 테스트 모범 사례는 Pest 5, TIA(Test Impact Analysis) 엔진, PHPUnit 13의 등장으로 크게 변화했습니다. Laravel 13은 Pest를 기본 지원하여 순수 PHPUnit보다 표현력 있고 간결한 테스트 경험을 제공합니다. 이 글에서는 프로덕션 애플리케이션과 기술 면접 모두에서 중요한 패턴을 다룹니다.
Pest 5(PHPUnit 13 기반)는 Laravel 13의 기본 테스트 프레임워크입니다. PHP 8.4 이상이 필요하며, TIA 엔진, 퍼스트파티 AI 에이전트 검증, PHPStan 통합, 시간 균형 샤딩이 기본으로 포함됩니다.
Laravel 13 프로젝트에서 Pest 5 설정
모든 새 Laravel 13 애플리케이션은 Pest가 자동으로 스캐폴딩됩니다. 기존 프로젝트의 경우 PHPUnit 13 호환성이 주요 고려 사항이므로 Pest 4에서의 마이그레이션은 몇 분이면 완료됩니다. 설정은 tests/Pest.php에 집중되며, 여기서 전역 트레이트와 헬퍼 메서드를 등록합니다.
use Illuminate\Foundation\Testing\RefreshDatabase;
pest()
->extend(Tests\TestCase::class)
->use(RefreshDatabase::class)
->in('Feature');이 단일 파일이 기존의 CreatesApplication 트레이트와 베이스 테스트 클래스 상속을 대체합니다. RefreshDatabase 트레이트는 각 테스트를 데이터베이스 트랜잭션으로 래핑하여 변경사항을 자동으로 롤백합니다.
use App\Models\User;
it('registers a new user with valid data', function () {
$response = $this->postJson('/api/register', [
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'password' => 'SecurePass123!',
'password_confirmation' => 'SecurePass123!',
]);
$response->assertStatus(201)
->assertJsonStructure(['user' => ['id', 'name', 'email']]);
expect(User::where('email', 'jane@example.com')->exists())->toBeTrue();
});Pest의 expect() API는 PHPUnit 어설션과 자연스럽게 연결됩니다. assertJsonStructure로 응답 구조를 검증하고, expect()->toBeTrue()로 데이터베이스 상태를 확인합니다. 두 스타일은 충돌 없이 공존합니다.
Test Impact Analysis: Pest 5의 핵심 기능
TIA 엔진은 대규모 코드베이스에서 Pest 5를 혁신적으로 만드는 기능입니다. 첫 번째 실행에서 Pest는 어떤 테스트가 어떤 파일에 영향을 미치는지 기록합니다. 이후 실행에서는 변경된 파일에 영향받는 테스트만 실행하고, 나머지는 캐시된 결과를 재사용합니다.
# 첫 번째 실행: 베이스라인 기록 (PCOV 또는 Xdebug 필요)
php artisan test
# 이후 실행: 영향받는 테스트만 실행
php artisan testLaravel Cloud의 테스트 스위트는 19,000개 이상의 테스트를 포함하며, TIA 활성화로 약 3분에서 5초로 단축되었습니다. 이 엔진은 PHP 파일뿐만 아니라 마이그레이션, Blade 템플릿, 공유 JavaScript 컴포넌트의 변경도 감지합니다.
TIA는 베이스라인을 기록하기 위해 커버리지 드라이버(PCOV 또는 Xdebug)가 필요합니다. CI 파이프라인에서는 캐시된 매핑이 실행 간에 유지되어 성능 향상 효과가 누적됩니다.
Laravel에서 단위 테스트와 기능 테스트의 구분
Laravel에서 단위 테스트와 기능 테스트의 구분은 프레임워크의 어떤 부분이 부팅되는지를 결정합니다. 단위 테스트는 애플리케이션 컨테이너 없이 실행되어 빠르지만 순수 로직에 제한됩니다. 기능 테스트는 전체 애플리케이션을 부팅하여 HTTP 호출, 데이터베이스 쿼리, 서비스 해결을 가능하게 합니다.
use App\Services\PriceCalculator;
describe('PriceCalculator', function () {
it('applies a percentage discount correctly', function () {
$calculator = new PriceCalculator();
// 150.00 기본 가격에서 20% 할인
$result = $calculator->applyDiscount(150.00, 20);
expect($result)->toBe(120.00);
});
it('rejects negative discount values', function () {
$calculator = new PriceCalculator();
expect(fn () => $calculator->applyDiscount(100.00, -5))
->toThrow(InvalidArgumentException::class);
});
});단위 테스트는 외부 의존성이 없는 독립된 클래스를 대상으로 합니다. describe 블록은 관련 어설션을 그룹화하고, Pest 5는 복잡한 테스트 계층을 위한 중첩 describe 블록을 지원합니다.
기능 테스트는 Laravel 테스트 스위트의 대부분을 차지해야 합니다. 잘못된 라우트 미들웨어, 누락된 유효성 검사 규칙, 깨진 Eloquent 관계와 같이 단위 테스트로는 놓치는 통합 버그를 잡습니다. 일반적인 규칙: 코드가 데이터베이스, HTTP 레이어 또는 파사드에 접근한다면 기능 테스트를 작성합니다.
파사드와 Mockery를 활용한 모킹 전략
Laravel 테스트에서 모킹은 테스트 대상 코드를 외부 의존성으로부터 격리합니다. Laravel 파사드는 큐, 이벤트, 알림, 메일, 스토리지를 위한 내장 페이크 구현을 제공합니다. Mockery는 그 외 모든 것을 처리합니다.
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use App\Mail\OrderConfirmation;
use App\Jobs\ProcessPayment;
it('dispatches payment job and sends confirmation email', function () {
// Mail과 Queue 파사드 모두 페이크
Mail::fake();
Queue::fake();
$user = User::factory()->create();
$order = Order::factory()->for($user)->create([
'total' => 99.99,
'status' => 'pending',
]);
// Act: HTTP를 통해 주문 확인
$this->actingAs($user)
->postJson("/api/orders/{$order->id}/confirm")
->assertOk();
// Assert: 결제 잡이 올바른 금액으로 디스패치됨
Queue::assertPushed(ProcessPayment::class, function ($job) use ($order) {
return $job->order->id === $order->id
&& $job->order->total === 99.99;
});
// Assert: 확인 이메일이 사용자에게 전송됨
Mail::assertSent(OrderConfirmation::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
});파사드 페이크는 프레임워크 수준에서 호출을 가로채어 실제 이메일 전송이나 잡 디스패치를 방지합니다. 클로저 기반 어설션으로 각 컴포넌트에 전달된 정확한 데이터를 검증합니다.
내부 도메인 클래스는 모킹을 피합니다. 경계에서 모킹: 서드파티 API, 메일, 큐, 파일시스템. 과도한 모킹은 테스트를 취약하게 만들고 구현 세부사항에 강하게 결합시킵니다.
파사드가 아닌 의존성의 경우 생성자를 통해 주입하고 Mockery를 사용합니다:
use App\Services\PaymentGateway;
use App\Services\StripeClient;
it('charges the customer through the payment gateway', function () {
// Stripe 클라이언트 모킹 생성
$stripeClient = Mockery::mock(StripeClient::class);
$stripeClient->shouldReceive('charge')
->once()
->with('cus_abc123', 5000, 'usd')
->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']);
// 컨테이너에 모킹 바인딩
$this->app->instance(StripeClient::class, $stripeClient);
$gateway = app(PaymentGateway::class);
$result = $gateway->processCharge('cus_abc123', 50.00);
expect($result['status'])->toBe('succeeded');
});$this->app->instance()로 모킹을 바인딩하면 테스트 기간 동안 실제 구현이 대체됩니다. Mockery는 charge가 예상 인수로 정확히 1회 호출되었는지 검증합니다.
Pest 프리셋을 활용한 아키텍처 테스트
Pest 5는 코드베이스 전체에서 구조적 규칙을 강제하는 아키텍처 테스트를 포함합니다. 이 테스트들은 런타임이 아닌 AST에 대해 실행되어 매우 빠릅니다.
arch('controllers do not use Eloquent directly')
->expect('App\Http\Controllers')
->not->toUse('Illuminate\Database\Eloquent');
arch('services are final classes')
->expect('App\Services')
->toBeFinal();
arch('no debugging functions in production code')
->expect(['dd', 'dump', 'var_dump', 'ray'])
->not->toBeUsed();첫 번째 규칙은 컨트롤러가 데이터베이스를 직접 쿼리하는 것을 금지하여 서비스 레이어 패턴을 강제합니다. 두 번째는 서비스가 확장될 수 없도록 하여 상속 복잡성을 줄입니다. 세 번째는 프로덕션에 도달하기 전에 남은 디버그 문을 감지합니다.
Laravel 전용 프리셋도 사용 가능합니다:
arch()->preset()->laravel();
arch()->preset()->security();
arch()->preset()->php();이 세 줄로 수십 개의 규칙을 강제합니다: 모델이 올바른 베이스 클래스를 상속하는지, 컨트롤러에 비즈니스 로직이 포함되어 있지 않은지, eval()이나 해싱을 위한 md5() 같은 안전하지 않은 함수가 없는지, 표준 PHP 규약이 준수되는지 검증합니다.
Laravel 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
뮤테이션 테스트로 테스트 품질 검증
코드 커버리지는 테스트 중 어떤 라인이 실행되었는지 측정합니다. 뮤테이션 테스트는 한 단계 더 나아갑니다: 소스 코드를 수정하고 테스트가 그 변경을 감지하는지 확인합니다. 뮤테이션이 생존하면 테스트 스위트에 결함이 있습니다.
# 특정 클래스에 대해 뮤테이션 테스트 실행
php artisan test --mutate --class=App\\Services\\PriceCalculatorPest 5는 >를 >=로 변경, return 문 제거, 불리언 조건 반전 같은 뮤테이션을 도입합니다. 뮤테이션 점수가 80% 미만이면 일반적으로 테스트가 엣지 케이스를 확인하지 않고 출력만 검증하고 있음을 나타냅니다.
// 예: 높은 커버리지지만 낮은 뮤테이션 점수를 가진 테스트
it('calculates shipping cost', function () {
$cost = calculateShipping(weight: 5.0, zone: 'domestic');
// 결과가 숫자인지만 확인
expect($cost)->toBeFloat();
});
// 개선됨: 정확한 값을 어설트하여 뮤테이션 포착
it('calculates domestic shipping for 5kg package', function () {
$cost = calculateShipping(weight: 5.0, zone: 'domestic');
// 정확한 어설션이 연산자와 값 뮤테이션을 포착
expect($cost)->toBe(12.50);
});첫 번째 테스트는 계산 로직이 완전히 잘못되어도 float를 반환하기만 하면 통과합니다. 두 번째 테스트는 수식에 뮤테이션이 발생하면 즉시 실패합니다. 구체적인 어설션이 더 높은 뮤테이션 점수를 만듭니다.
데이터베이스 테스트 패턴과 팩토리
Laravel 모델 팩토리는 수동 배열 구성 없이 현실적인 테스트 데이터를 생성합니다. Pest 5와 팩토리를 결합하면 가독성 높고 유지보수하기 쉬운 데이터 설정이 가능합니다.
use App\Models\Article;
use App\Models\User;
it('publishes a draft article and updates the timestamp', function () {
$author = User::factory()->create(['role' => 'editor']);
$article = Article::factory()
->for($author, 'author')
->draft()
->create(['title' => 'Testing Best Practices']);
$this->actingAs($author)
->patchJson("/api/articles/{$article->id}/publish")
->assertOk()
->assertJsonPath('data.status', 'published');
$article->refresh();
expect($article->status)->toBe('published')
->and($article->published_at)->not->toBeNull()
->and($article->published_at->isToday())->toBeTrue();
});draft() 팩토리 상태는 미발행 기사의 기본값을 설정합니다. expect() 어설션을 ->and()로 체이닝하면 자연어처럼 읽히고 설명적인 메시지로 실패합니다.
php artisan test --parallel을 실행하면 테스트가 여러 프로세스에서 실행됩니다. Laravel은 각 프로세스에 별도의 테스트 데이터베이스를 자동으로 생성하여 데이터 충돌을 방지합니다. Pest 5의 시간 균형 샤딩은 개수가 아닌 실제 실행 시간으로 테스트를 분산하여 모든 CI 샤드가 동시에 완료되도록 합니다.
Laravel 테스트 관련 일반적인 면접 질문
Laravel 포지션 기술 면접에서는 테스트 지식을 자주 확인합니다. 가장 많이 나오는 질문과 좋은 답변이 다루어야 할 내용입니다.
Laravel 테스트에서 fake(), mock(), spy()의 차이점은 무엇입니까?
파사드 fake()는 전체 파사드를 인메모리 구현으로 대체합니다(Mail::fake, Queue::fake). Mockery를 통한 mock()은 실행 전에 기대치를 설정하고 충족되지 않으면 실패합니다. spy()는 사전 기대치 없이 상호작용을 기록하고 실행 후 어설션을 허용합니다. 테스트가 동작을 검증해야 하는지(mock), 상호작용을 기록하는지(spy), 또는 부작용을 방지하는지(fake)에 따라 선택합니다.
RefreshDatabase와 DatabaseTransactions의 차이점은 무엇입니까?
RefreshDatabase는 마이그레이션을 한 번 실행하고 각 테스트를 트랜잭션으로 래핑합니다. DatabaseTransactions는 데이터베이스에 이미 올바른 스키마가 있다고 가정하고 테스트만 트랜잭션으로 래핑합니다. RefreshDatabase는 데이터베이스가 아직 존재하지 않을 수 있는 CI 파이프라인에서 더 안전합니다. DatabaseTransactions는 스키마가 최신임이 보장될 때 더 빠릅니다.
기능 테스트와 단위 테스트 중 언제 어떤 것을 선호해야 합니까?
기능 테스트는 HTTP 레이어, 데이터베이스 또는 Laravel 서비스와 상호작용하는 모든 코드를 다뤄야 합니다. 단위 테스트는 프레임워크 의존성이 없는 순수 함수와 값 객체에 예약됩니다. 일반적인 Laravel 애플리케이션에서 기능 테스트가 단위 테스트를 약 3:1 이상의 비율로 앞섭니다. 이는 대부분의 Laravel 코드가 본질적으로 프레임워크와 통합되어 있다는 현실을 반영합니다.
Test Impact Analysis는 CI 성능을 어떻게 개선합니까?
TIA는 첫 번째 실행에서 어떤 테스트가 어떤 소스 파일에 접근하는지 기록합니다. 이후 실행에서 Pest는 변경된 파일에 영향받는 테스트만 실행하고 변경되지 않은 테스트에는 캐시된 결과를 재사용합니다. 몇 분 걸리던 대규모 스위트가 몇 초 만에 완료될 수 있습니다. 단점: TIA는 의존성 맵을 구축하기 위해 커버리지 드라이버(PCOV 또는 Xdebug)가 필요하며, 첫 번째 실행은 일반 실행보다 오래 걸립니다.
뮤테이션 테스트는 코드 커버리지를 넘어 테스트 품질을 어떻게 향상시킵니까?
코드 커버리지는 실행 경로를 측정합니다. 테스트는 의미 있는 어설션 없이 모든 메서드를 호출하여 100% 커버리지를 달성할 수 있습니다. 뮤테이션 테스트는 소스 코드를 수정(연산자 변경, return 제거, 불리언 반전)하고 적어도 하나의 테스트가 실패하는지 확인합니다. 생존한 뮤테이션은 어설션이 너무 느슨하거나 완전히 누락되었음을 드러냅니다. php artisan test --mutate를 실행하면 표준 커버리지와 함께 뮤테이션 점수 퍼센트가 생성됩니다.
더 많은 Laravel 면접 질문은 SharpSkill 질문 뱅크에서 인증, 서비스 컨테이너 패턴, Eloquent 관계, 큐 아키텍처를 다룹니다.
HTTP 응답 및 JSON 어설션 테스트
Laravel의 HTTP 테스트 헬퍼는 상태 코드, 헤더, JSON 구조, 리디렉션 대상을 검증합니다. Pest와 결합하면 간결한 통합 테스트를 형성합니다.
use App\Models\User;
describe('API Authentication', function () {
it('rejects unauthenticated requests with 401', function () {
$this->getJson('/api/profile')
->assertUnauthorized();
});
it('returns the authenticated user profile', function () {
$user = User::factory()->create([
'name' => 'John Doe',
'email' => 'john@example.com',
]);
$this->actingAs($user)
->getJson('/api/profile')
->assertOk()
->assertJson([
'data' => [
'name' => 'John Doe',
'email' => 'john@example.com',
],
]);
});
it('validates required fields on registration', function () {
$this->postJson('/api/register', [])
->assertUnprocessable()
->assertJsonValidationErrors(['name', 'email', 'password']);
});
});describe 블록은 인증 관련 테스트를 그룹화합니다. 각 테스트 이름은 구현이 아닌 예상 동작을 설명합니다. assertJsonValidationErrors 메서드는 특정 필드에 유효성 검사 오류 메시지가 있는지 확인합니다.
Sources
- Pest 5 Released: TIA 엔진, Agent 플러그인, Evals를 발표한 Laravel News
- Pest 5 Now Available: 요구사항과 새 기능을 담은 공식 문서
- What We Know About Laravel 13: PHP Attributes, AI SDK, 2026년 3월 릴리스
- PHPUnit 13 Release Announcement: 새 어설션과 지원 중단 상세
Pest 5 Laravel 테스트에서 기억할 점
- Pest 5와 Laravel 13은 유창한 설정 API,
expect()체이닝, 자동 테스트 검색으로 보일러플레이트를 제거합니다 - TIA 엔진은 영향받는 테스트만 재실행하여 커버리지 정확도를 희생하지 않고 대규모 스위트 실행을 몇 분에서 몇 초로 단축합니다
- 기능 테스트가 Laravel 테스트 스위트의 핵심을 형성하고, 단위 테스트는 격리된 비즈니스 로직에 예약됩니다
- 파사드 페이크(
Mail::fake(),Queue::fake())는 프레임워크 수준 모킹을 처리하고, Mockery는 컨테이너를 통해 주입된 서드파티 의존성을 처리합니다 - 아키텍처 테스트는 구조적 규칙(컨트롤러에서 Eloquent 금지, 디버그 함수 금지)을 런타임 오버헤드 없이 강제합니다
--mutate를 사용한 뮤테이션 테스트는 코드 커버리지만으로는 놓치는 약한 어설션을 잡습니다- 팩토리 상태와
expect()->and()어설션 체이닝으로 테스트 데이터 설정이 가독성을 유지하고 어설션이 구체적이 됩니다 - Laravel 면접 준비에서 mock/fake/spy,
RefreshDatabase동작, TIA, 뮤테이션 테스트 이해는 기본 커버리지를 넘어선 테스트 성숙도를 보여줍니다
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
Laravel 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 9월 2일 업데이트
태그
공유
관련 기사

Laravel 솔루션: 고급 패턴, 디버깅 및 면접 대비 2026년판
Laravel의 고급 아키텍처 패턴, 디버깅 기법, 면접 대비 지식을 완벽하게 마스터합니다. 서비스 클래스, 레포지토리 패턴, Telescope를 활용한 디버깅, Laravel 개발자를 위한 자주 나오는 면접 질문을 상세히 다룹니다.

Laravel 12 새로운 기능 총정리: 스타터 킷, WorkOS AuthKit, 업그레이드 가이드 및 면접 질문 (2026)
Laravel 12의 새로운 기능을 상세히 분석합니다. 스타터 킷 개편, WorkOS AuthKit 통합, Carbon 3 필수화, 업그레이드 가이드, 그리고 2026년 Laravel 면접 질문까지 실무에 필요한 모든 내용을 다룹니다.

Laravel과 PHP 면접 질문: 2026년 핵심 25선
Laravel과 PHP 면접에서 가장 자주 출제되는 25가지 질문을 상세히 다룹니다. Eloquent ORM, 미들웨어, 큐, 테스트, 아키텍처 패턴에 대한 상세한 답변과 코드 예제를 제공합니다.