본문 바로가기

모킹

번역일: 2026년 6월 20일

모킹

소개

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

Laravel은 이벤트, Job, 파사드 등을 모킹하는 편리한 헬퍼 메서드를 기본으로 제공합니다. 이 헬퍼들은 Mockery 위에 편의 레이어를 얹은 것으로, 복잡한 Mockery 메서드 호출을 직접 작성하지 않아도 됩니다.

객체 모킹

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

use App\Service; use Mockery; use Mockery\MockInterface; public function test_something_can_be_mocked(): void { $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { $mock->shouldReceive('process')->once(); }) ); }

더 간결하게 작성하려면, Laravel 기본 테스트 클래스에서 제공하는 mock 메서드를 사용할 수 있습니다. 아래 예시는 위 코드와 동일하게 동작합니다.

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

일부 메서드만 모킹하고 나머지는 실제로 실행하고 싶다면 partialMock 메서드를 사용하세요. 모킹하지 않은 메서드는 정상적으로 호출됩니다.

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

객체를 스파이(spy)하고 싶다면 spy 메서드를 사용할 수 있습니다. 스파이는 모킹과 비슷하지만, 코드 실행 중 발생한 모든 상호작용을 기록한다는 점이 다릅니다. 덕분에 코드가 실행된 이후에 어떤 메서드가 호출됐는지 검증할 수 있습니다.

NOTE

모킹 vs 스파이: 모킹은 코드 실행 전에 기대값을 미리 선언하고, 스파이는 코드 실행 후에 실제로 무엇이 호출됐는지 확인하는 방식입니다. 검증 타이밍이 다르므로 상황에 맞게 선택하세요.

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

파사드 모킹

일반적인 정적 메서드 호출과 달리, Laravel의 파사드(실시간 파사드 포함)는 모킹이 가능합니다. 파사드는 서비스 컨테이너를 통해 해석되기 때문에, 전통적인 의존성 주입과 동일한 수준의 테스트 용이성을 제공합니다.

예를 들어, 다음과 같은 컨트롤러 액션이 있다고 가정해 보겠습니다.

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

shouldReceive 메서드를 사용해 Cache 파사드의 호출을 모킹할 수 있습니다. 이 메서드는 Mockery 모킹 인스턴스를 반환합니다. 아래 예시는 Cache::get 메서드 호출을 모킹하는 방법입니다.

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

WARNING

Request 파사드는 모킹하지 마세요. 대신, 테스트 실행 시 get, post 같은 HTTP 테스트 메서드에 원하는 입력값을 직접 전달하세요. 마찬가지로, Config 파사드도 모킹 대신 Config::set 메서드를 사용해 설정값을 변경하세요.

파사드 스파이

파사드에도 스파이를 적용할 수 있습니다. 해당 파사드에서 spy 메서드를 호출하면 됩니다. 스파이는 코드 실행 중 발생한 모든 상호작용을 기록하므로, 실행 이후 검증이 가능합니다.

use Illuminate\Support\Facades\Cache; public function test_values_are_be_stored_in_cache(): void { Cache::spy(); $response = $this->get('/'); $response->assertStatus(200); Cache::shouldHaveReceived('put')->once()->with('name', 'Taylor', 10); }

시간 조작

테스트를 작성하다 보면 now()Illuminate\Support\Carbon::now()가 반환하는 시간을 임의로 변경해야 할 때가 있습니다. 예를 들어 "7일 비활동 시 게시글 잠금" 같은 기능은 실제 시간이 흐르기를 기다릴 수 없으니까요. Laravel의 기본 피처 테스트 클래스는 이를 위한 시간 조작 헬퍼를 제공합니다.

use Illuminate\Support\Carbon; public function test_time_can_be_manipulated(): 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()->subHours(6)); // 현재 시간으로 복귀 $this->travelBack(); }

시간 이동 메서드에 클로저를 전달하면, 지정한 시점에 시간이 고정된 상태로 클로저가 실행됩니다. 클로저 실행이 끝나면 시간은 자동으로 정상 흐름으로 복귀합니다.

$this->travel(5)->days(function () { // 5일 후의 시점에서 테스트 실행 }); $this->travelTo(now()->subDays(10), function () { // 10일 전 시점에서 테스트 실행 });

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

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

이러한 시간 조작 헬퍼는 시간에 민감한 비즈니스 로직을 테스트할 때 특히 유용합니다. 예를 들어 포럼에서 1주일간 활동이 없는 게시글을 자동으로 잠그는 기능을 다음과 같이 테스트할 수 있습니다.

use App\Models\Thread; public function test_forum_threads_lock_after_one_week_of_inactivity() { $thread = Thread::factory()->create(); $this->travel(1)->week(); $this->assertTrue($thread->isLockedByInactivity()); }

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

번역일: 2026년 6월 20일