본문 바로가기

모킹

번역일: 2026년 6월 20일

모킹

소개

Laravel 애플리케이션을 테스트할 때, 특정 동작이 실제로 실행되지 않도록 "모킹(mocking)"하고 싶은 경우가 있습니다. 예를 들어, 이벤트를 발생시키는 컨트롤러를 테스트할 때 이벤트 리스너가 실제로 실행되지 않도록 모킹할 수 있습니다. 이렇게 하면 리스너의 동작은 별도의 테스트에서 검증하고, 컨트롤러의 HTTP 응답만 집중적으로 테스트할 수 있습니다.

Laravel은 이벤트, Job, 각종 파사드를 간편하게 모킹할 수 있는 헬퍼를 기본 제공합니다. 이 헬퍼들은 내부적으로 Mockery를 사용하므로, 복잡한 Mockery 메서드를 직접 작성하지 않아도 됩니다.

객체 모킹

Laravel의 서비스 컨테이너를 통해 주입되는 객체를 모킹할 때는, 모킹한 인스턴스를 컨테이너에 instance 바인딩으로 등록해야 합니다. 이렇게 하면 컨테이너가 해당 객체를 직접 생성하는 대신 모킹된 인스턴스를 사용합니다.

Pest

use App\Service; use Mockery; use Mockery\MockInterface; test('객체를 모킹할 수 있다', function () { $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { $mock->expects('process'); }) ); });

PHPUnit

use App\Service; use Mockery; use Mockery\MockInterface; public function test_객체를_모킹할__있다(): void { $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { $mock->expects('process'); }) ); }

더 간결하게 작성하려면 Laravel 기본 테스트 클래스의 mock 메서드를 사용하세요. 아래 예시는 위와 동일하게 동작합니다.

use App\Service; use Mockery\MockInterface; $mock = $this->mock(Service::class, function (MockInterface $mock) { $mock->expects('process'); });

객체의 일부 메서드만 모킹하고 나머지는 실제 구현을 실행하고 싶다면 partialMock 메서드를 사용하세요.

use App\Service; use Mockery\MockInterface; $mock = $this->partialMock(Service::class, function (MockInterface $mock) { $mock->expects('process'); });

스파이(spy)가 필요한 경우에는 spy 메서드를 사용할 수 있습니다. 스파이는 모킹과 유사하지만, 코드 실행 이후에 어떤 상호작용이 있었는지 검증하는 방식입니다. 모킹이 "이 메서드가 호출될 것이다"를 미리 선언하는 방식이라면, 스파이는 "실제로 이 메서드가 호출됐는가"를 사후에 확인하는 방식입니다.

use App\Service; $spy = $this->spy(Service::class); // ... 테스트 코드 실행 ... $spy->shouldHaveReceived('process');

파사드 모킹

일반적인 정적 메서드 호출과 달리, 파사드(실시간 파사드 포함)는 모킹이 가능합니다. 파사드는 내부적으로 서비스 컨테이너를 통해 처리되기 때문에, 일반 정적 클래스보다 훨씬 유연하게 테스트할 수 있습니다.

예를 들어 다음과 같은 컨트롤러가 있다고 가정합니다.

<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; class UserController extends Controller { /** * 애플리케이션의 모든 사용자 목록을 반환합니다. */ public function index(): array { $value = Cache::get('key'); return [ // ... ]; } }

expects 메서드를 사용하면 Cache 파사드의 호출을 모킹할 수 있습니다. 이 메서드는 Mockery 모킹 인스턴스를 반환합니다.

Pest

<?php use Illuminate\Support\Facades\Cache; test('index 조회', function () { Cache::expects('get') ->with('key') ->andReturn('value'); $response = $this->get('/users'); // ... });

PHPUnit

<?php namespace Tests\Feature; use Illuminate\Support\Facades\Cache; use Tests\TestCase; class UserControllerTest extends TestCase { public function test_index_조회(): void { Cache::expects('get') ->with('key') ->andReturn('value'); $response = $this->get('/users'); // ... } }

WARNING

Request 파사드는 모킹하지 마세요. 대신 get, postHTTP 테스트 메서드에 원하는 입력값을 직접 전달하세요. 마찬가지로 Config 파사드를 모킹하는 대신, 테스트 내에서 Config::set 메서드를 호출하여 설정값을 변경하세요.

파사드 스파이

파사드에 스파이를 적용하려면 해당 파사드의 spy 메서드를 호출하면 됩니다. 스파이는 코드 실행 후 파사드와의 상호작용을 검증하는 방식으로, 실행 전에 기대값을 선언하는 모킹과 차이가 있습니다.

Pest

<?php use Illuminate\Support\Facades\Cache; test('값이 캐시에 저장된다', function () { Cache::spy(); $response = $this->get('/'); $response->assertStatus(200); Cache::shouldHaveReceived('put')->with('name', 'Taylor', 10); });

PHPUnit

use Illuminate\Support\Facades\Cache; public function test_값이_캐시에_저장된다(): void { Cache::spy(); $response = $this->get('/'); $response->assertStatus(200); Cache::shouldHaveReceived('put')->with('name', 'Taylor', 10); }

시간 조작

테스트 중에는 now()Illuminate\Support\Carbon::now()가 반환하는 현재 시간을 임의로 변경해야 하는 경우가 있습니다. Laravel의 기본 기능 테스트 클래스는 현재 시간을 자유롭게 조작할 수 있는 헬퍼를 제공합니다.

Pest

test('시간을 조작할 수 있다', function () { // 미래로 이동... $this->travel(5)->milliseconds(); $this->travel(5)->seconds(); $this->travel(5)->minutes(); $this->travel(5)->hours(); $this->travel(5)->days(); $this->travel(5)->weeks(); $this->travel(5)->years(); // 과거로 이동... $this->travel(-5)->hours(); // 특정 시점으로 이동... $this->travelTo(now()->minus(hours: 6)); // 현재 시간으로 복귀... $this->travelBack(); });

PHPUnit

public function test_시간을_조작할__있다(): void { // 미래로 이동... $this->travel(5)->milliseconds(); $this->travel(5)->seconds(); $this->travel(5)->minutes(); $this->travel(5)->hours(); $this->travel(5)->days(); $this->travel(5)->weeks(); $this->travel(5)->years(); // 과거로 이동... $this->travel(-5)->hours(); // 특정 시점으로 이동... $this->travelTo(now()->minus(hours: 6)); // 현재 시간으로 복귀... $this->travelBack(); }

시간 이동 메서드에 클로저를 전달하면, 클로저 실행 중에만 시간이 고정되고 실행이 끝나면 자동으로 원래 시간으로 돌아옵니다.

$this->travel(5)->days(function () { // 5일 후의 상황을 테스트... }); $this->travelTo(now()->subDays(10), function () { // 특정 시점의 상황을 테스트... });

현재 시간을 완전히 고정하려면 freezeTime 메서드를 사용하세요. freezeSecond는 현재 초의 시작 시점에서 시간을 고정합니다.

use Illuminate\Support\Carbon; // 클로저 실행 중 시간을 고정하고, 실행 후 정상 흐름으로 복귀... $this->freezeTime(function (Carbon $time) { // ... }); // 현재 초의 시작 시점에서 시간을 고정하고, 실행 후 정상 흐름으로 복귀... $this->freezeSecond(function (Carbon $time) { // ... });

시간 조작 기능은 시간에 민감한 비즈니스 로직을 테스트할 때 특히 유용합니다. 예를 들어, 일정 기간 동안 활동이 없는 게시글 스레드를 자동으로 잠그는 기능을 테스트하는 경우를 살펴보겠습니다.

Pest

use App\Models\Thread; test('게시글 스레드는 1주일 동안 활동이 없으면 잠긴다', function () { $thread = Thread::factory()->create(); $this->travel(1)->week(); expect($thread->isLockedByInactivity())->toBeTrue(); });

PHPUnit

use App\Models\Thread; public function test_게시글_스레드는_1주일_동안_활동이_없으면_잠긴다() { $thread = Thread::factory()->create(); $this->travel(1)->week(); $this->assertTrue($thread->isLockedByInactivity()); }

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 20일