메인 콘텐츠로 건너뛰기

테스트란

테스트란, 코드가 기대한 대로 동작하는지를 자동적으로 검증하는 구조입니다. 테스트를 작성함으로써, 기능 추가나 수정을 했을 때에 기존의 동작이 무너지지 않았는지를 신속하게 확인할 수 있습니다. 팀에서의 개발에서는, 테스트가 있음으로써 안심하고 코드를 변경·리뷰할 수 있습니다. Laravel은 처음부터 테스트의 지원이 포함되어 있어, PestPHPUnit의 양쪽을 사용할 수 있습니다. 신규 설치 시에는 phpunit.xml 설정 파일과 tests/ 디렉터리가 자동적으로 준비되어 있습니다.
Pest는 PHPUnit을 기반으로 구축되어 있어, 보다 간결하고 읽기 쉬운 구문으로 테스트를 작성할 수 있습니다. 지금부터 테스트를 시작하는 경우는 Pest를 선택하는 것이 추천됩니다.

tests/ 디렉터리의 구성

tests/
├── Feature/      # 기능 테스트
│   └── ExampleTest.php
├── Unit/         # 유닛 테스트
│   └── ExampleTest.php
└── TestCase.php
  • Feature/ — 기능 테스트를 둡니다. HTTP 리퀘스트를 포함하는 큰 단위의 테스트에 사용합니다. 여러 오브젝트가 연계하는 동작이나 API 엔드포인트의 검증 등, 애플리케이션 전체에 가까운 테스트입니다. 대부분의 테스트는 여기에 두게 됩니다.
  • Unit/ — 유닛 테스트를 둡니다. 단일의 클래스나 메서드 등, 작은 단위의 테스트에 사용합니다. Laravel 애플리케이션은 기동하지 않기 때문에, 데이터베이스나 다른 프레임워크 기능은 사용할 수 없습니다.

테스트의 작성

make:test Artisan 명령으로 새로운 테스트 클래스를 생성합니다.
# Feature 테스트를 작성 (기본값)
php artisan make:test TodoTest

# Unit 테스트를 작성
php artisan make:test TodoTest --unit
tests/Feature/TodoTest.php가 생성됩니다.

테스트의 실행

php artisan test 명령으로 테스트를 실행합니다.
php artisan test
vendor/bin/pestvendor/bin/phpunit을 직접 실행해도 같은 결과를 얻을 수 있지만, php artisan test 쪽이 보기 좋은 출력을 얻을 수 있기 때문에 추천합니다. 특정한 테스트 스위트만을 실행하고 싶을 때는 옵션을 사용합니다.
# Feature 테스트만 실행
php artisan test --testsuite=Feature

# 실패하면 즉시 정지
php artisan test --stop-on-failure

기본적인 테스트 작성 방법

심플한 어서션의 예입니다.
<?php

test('true는 true이다', function () {
    expect(true)->toBeTrue();
});

test('문자열이 포함되어 있는지 확인', function () {
    $message = 'Hello, Laravel!';

    expect($message)->toContain('Laravel');
});
<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    public function test_true_is_true(): void
    {
        $this->assertTrue(true);
    }

    public function test_string_contains_laravel(): void
    {
        $message = 'Hello, Laravel!';

        $this->assertStringContainsString('Laravel', $message);
    }
}

자주 사용하는 어서션

PestPHPUnit설명
expect($x)->toBe($y)$this->assertSame($y, $x)엄밀히 같음
expect($x)->toEqual($y)$this->assertEquals($y, $x)같음 (타입은 상관없음)
expect($x)->toBeTrue()$this->assertTrue($x)true
expect($x)->toBeFalse()$this->assertFalse($x)false
expect($x)->toBeNull()$this->assertNull($x)null
expect($x)->toContain($y)$this->assertStringContainsString($y, $x)문자열을 포함
expect($x)->toHaveCount($n)$this->assertCount($n, $x)요소수가 일치

HTTP 테스트

Laravel의 HTTP 테스트에서는, 실제의 HTTP 서버를 세우지 않고, 라우트에의 리퀘스트를 시뮬레이트할 수 있습니다. Feature/ 디렉터리의 테스트 클래스에서 사용할 수 있는 강력한 기능입니다.

페이지의 표시를 확인

get() 메서드로 GET 리퀘스트를 보내고, 응답을 검증합니다.
<?php

test('톱 페이지가 표시된다', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_top_page_is_displayed(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

ToDo 앱의 HTTP 테스트 예

Todo 모델이 있는 애플리케이션을 예로, CRUD의 각 조작을 테스트합니다.
<?php

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('ToDo의 일람 페이지가 표시된다', function () {
    Todo::factory()->count(3)->create();

    $response = $this->get('/todos');

    $response->assertStatus(200);
    $response->assertSee('일람');
});

test('ToDo를 신규 작성할 수 있다', function () {
    $response = $this->post('/todos', [
        'title' => 'Laravel을 배운다',
    ]);

    $response->assertRedirect('/todos');
    $this->assertDatabaseHas('todos', ['title' => 'Laravel을 배운다']);
});

test('ToDo를 삭제할 수 있다', function () {
    $todo = Todo::factory()->create();

    $response = $this->delete("/todos/{$todo->id}");

    $response->assertRedirect('/todos');
    $this->assertDatabaseMissing('todos', ['id' => $todo->id]);
});
<?php

namespace Tests\Feature;

use App\Models\Todo;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TodoTest extends TestCase
{
    use RefreshDatabase;

    public function test_todo_list_is_displayed(): void
    {
        Todo::factory()->count(3)->create();

        $response = $this->get('/todos');

        $response->assertStatus(200);
        $response->assertSee('일람');
    }

    public function test_todo_can_be_created(): void
    {
        $response = $this->post('/todos', [
            'title' => 'Laravel을 배운다',
        ]);

        $response->assertRedirect('/todos');
        $this->assertDatabaseHas('todos', ['title' => 'Laravel을 배운다']);
    }

    public function test_todo_can_be_deleted(): void
    {
        $todo = Todo::factory()->create();

        $response = $this->delete("/todos/{$todo->id}");

        $response->assertRedirect('/todos');
        $this->assertDatabaseMissing('todos', ['id' => $todo->id]);
    }
}

자주 사용하는 응답의 어서션

메서드설명
assertStatus(200)응답의 HTTP 스테이터스 코드를 확인
assertOk()스테이터스 코드가 200임을 확인
assertRedirect('/path')지정 URL에의 리다이렉트를 확인
assertSee('텍스트')응답 본문에 텍스트가 포함되는지 확인
assertDontSee('텍스트')응답 본문에 텍스트가 포함되지 않는지 확인
assertJson([...])JSON 응답의 데이터를 확인
assertDatabaseHas('table', [...])데이터베이스에 레코드가 존재하는지 확인
assertDatabaseMissing('table', [...])데이터베이스에 레코드가 존재하지 않는지 확인
RefreshDatabase 트레이트를 사용하면, 각 테스트의 실행 후에 데이터베이스를 리셋합니다. 테스트 간에 데이터가 간섭하지 않도록 하기 위해 사용합니다.

테스트 환경

phpunit.xml의 설정

프로젝트 루트의 phpunit.xml에서 테스트 환경의 설정을 수행합니다. 기본적으로 세션과 캐시가 array 드라이버로 설정되어 있어, 테스트 실행 중에 데이터가 남지 않습니다.
<env name="APP_ENV" value="testing"/>
<env name="CACHE_STORE" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
데이터베이스에는, 파일을 남기지 않는 SQLite의 인메모리 데이터베이스를 사용하는 것이 일반적입니다.
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>

.env.testing 파일

프로젝트 루트에 .env.testing 파일을 작성하면, 테스트 실행 시에 .env 대신에 로드됩니다. 테스트 전용의 데이터베이스 접속이나 외부 서비스의 설정을 나누고 싶은 경우에 사용합니다.
APP_ENV=testing
DB_CONNECTION=sqlite
DB_DATABASE=:memory:
설정을 캐시하고 있는 경우, php artisan config:clear를 실행한 후에 테스트를 실행해 주세요. 오래된 캐시가 사용되어 설정이 반영되지 않을 수 있습니다.

병렬 테스트 실행

테스트 수가 늘어나면 실행 시간이 길어집니다. --parallel 옵션을 사용하면, 여러 프로세스에서 테스트를 동시 실행해 시간을 단축할 수 있습니다. 우선 brianium/paratest 패키지를 설치합니다.
composer require brianium/paratest --dev
그 후, --parallel 옵션을 붙여 실행합니다.
php artisan test --parallel
기본적으로 CPU의 코어 수만큼의 프로세스가 기동합니다. 프로세스 수를 지정하려면 --processes 옵션을 사용합니다.
php artisan test --parallel --processes=4
병렬 테스트를 사용할 때는, 각 프로세스가 독립된 데이터베이스를 사용해야 합니다. RefreshDatabase 트레이트와 phpunit.xml의 인메모리 SQLite 설정을 조합하면 문제없이 동작합니다.

다음 단계

HTTP 테스트

리퀘스트의 시뮬레이션, 인증 첨부 테스트, JSON 어서션 등, 보다 자세한 HTTP 테스트의 방법을 확인할 수 있습니다.
마지막 수정일 2026년 7월 13일