애플리케이션 테스팅

시작하기

라라벨은 애플리케이션에 HTTP request-요청을 하고, 결과를 검사하고, 또한 form을 채우는 매우 다양한 사용이 가능한 API를 제공합니다. 다음에 정의된 테스트 예제를 살펴보겠습니다:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->visit('/')
             ->see('Laravel 5')
             ->dontSee('Rails');
    }
}

visit 메소드는 애플리케이션에 GET request-요청을 만듭니다. see 메소드는 애플리케이션에서 반환된 reponse-응답에 주어진 텍스트가 있는지 확인합니다. dontSee 메소드는 주어진 텍스트가 애플리케이션 response-응답으로 반환되지 않았다는 것을 확인합니다. 이것은 라라벨이 제공하는 가장 기본적인 애플리케이션 테스트입니다.

또한 이름이 지정된 라우트에 대한 GET request를 만들기 위해서 visitRoute 메소드를 사용할 수 있습니다:

$this->visitRoute('profile');

$this->visitRoute('profile', ['user' => 1]);

애플리케이션과 상호작용하기

물론 단순하게 주어진 응답에 텍스트가 나타날지 확인하는 것 이상의 테스트가 가능합니다. 링크 클릭하는 것과 form을 채워넣는 예제들을 살펴보겠습니다:

링크를 통한 상호작용

이 테스트에서는 애플리케이션에 요청을 보내고, 응답으로 돌아온 링크를 "클릭"하여, 주어진 URI로 이동하도록 할 것입니다. 예를 들어 응답으로 "About Us"이라는 텍스트 값을 가진 링크가 왔다고 가정해봅시다:

<a href="/about-us">About Us</a>

이제 링크를 클릭해 사용자가 알맞은 페이지로 이동했는지 확인하는 테스트를 작성해 봅시다:

public function testBasicExample()
{
    $this->visit('/')
         ->click('About Us')
         ->seePageIs('/about-us');
}

또한 seeRouteIs 메소드를 사용하여 사용자가 이름이 지정된 라우트에 도착했는지 확인할 수 있습니다.

->seeRouteIs('profile', ['user' => 1]);

Form을 사용한 상호작용

라라벨은 또한 form을 테스트하는 여러 메소드들을 제공합니다. type, select, check, attach, 그리고 press 메소드는 form의 모든 input들과 상호작용할 수 있도록 해줍니다. 예를 들어 이 form이 애플리케이션의 등록 페이지에 존재한다고 가정해보겠습니다:

<form action="/register" method="POST">
    {{ csrf_field() }}

    <div>
        Name: <input type="text" name="name">
    </div>

    <div>
        <input type="checkbox" value="yes" name="terms"> Accept Terms
    </div>

    <div>
        <input type="submit" value="Register">
    </div>
</form>

이 form을 완성하고 결과를 검사할 수 있는 테스트를 작성할 수 있습니다:

public function testNewUserRegistration()
{
    $this->visit('/register')
         ->type('Taylor', 'name')
         ->check('terms')
         ->press('Register')
         ->seePageIs('/dashboard');
}

물론 form이 라디오 버튼이나 드롭다운 박스와 같은 다른 input을 가지고 있다면 이런 종류의 필드도 손쉽게 채울 수 있습니다. 다음은 각각의 form을 조작하는 메소드 목록입니다:

메소드 설명
$this->type($text, $elementName) 주어진 필드에 텍스트를 "채워넣음"
$this->select($value, $elementName) 라디오 버튼 또는 드롭다운 필드를 "선택"
$this->check($elementName) 체크박스 필드를 "체크"
$this->uncheck($elementName) 체크박스 필드를 "언체크"
$this->attach($pathToFile, $elementName) 파일을 form에 "추가"
$this->press($buttonTextOrElementName) 주어진 텍스 또는 이름의 버튼을 "누르기"

파일 Input

form이 file input을 가지고 있다면, attach 메소드를 이용하여 form에 파일을 첨부할 수 있습니다:

public function testPhotoCanBeUploaded()
{
    $this->visit('/upload')
         ->attach($pathToFile, 'photo')
         ->press('Upload')
         ->see('Upload Successful!');
}

JSON API 테스팅하기

라라벨은 또한 JSON API와 그 결과를 테스트하기 위해 여러 헬퍼들을 제공합니다. 예를 들어, json, get, post, put, patch, 그리고 delete 메소드들을 이용하여 다양한 HTTP verb를 가진 request-요청을 할 수 있습니다. 이 메소드들에 손쉽게 데이터와 헤더를 전달할 수도 있습니다. 이를 위해 /userPOST request-요청을 하고 원하는 데이터가 반환되는지 확인하는 테스트를 작성해보겠습니다:

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->json('POST', '/user', ['name' => 'Sally'])
             ->seeJson([
                 'created' => true,
             ]);
    }
}

{팁} seeJson 메소드는 주어진 배열을 JSON으로 변환하고 변환된 JSON이 애플리케이션이 반환하는 JSON 응답 중 어느 한 곳에 존재하는 것을 확인합니다. 따라서 JSON response-응답이 다른 속성을 가지고 있어도 특정 부분이 있기만 하면 테스트는 통과할 것입니다.

정확하게 일치하는지 확인하기

주어진 배열이 반환된 JSON과 정확히 일치하는지 확인하고자 한다면, seeJsonEquals 메소드를 사용하면 됩니다:

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->json('POST', '/user', ['name' => 'Sally'])
             ->seeJsonEquals([
                 'created' => true,
             ]);
    }
}

구조적으로 일치하는지 확인하기

JSON 응답이 지정한 구조에 부합하는지 확인하는 것도 가능합니다. 이 시나리오에서는 seeJsonStructure 메소드를 사용할 것이며, 기대되는 JSON 구조를 전달해야 합니다:

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->get('/user/1')
             ->seeJsonStructure([
                 'name',
                 'pet' => [
                     'name', 'age'
                 ]
             ]);
    }
}

위의 예제는 "name" 과 "중첩된 nameage를 가지는 pet 객체"를 반환할 것이라고 예상하고 있습니다. 응답에 추가적인 키가 존재한다 해도 seeJsonStructure 에서는 실패로 간주되지 않습니다. 예를 들어 petweight 속성을 가지고 있다고 해도 테스트는 여전히 통과할 것입니다.

반환된 JSON 구조가 각 목록 아이템 값의 세트에서 주어진 속성을 반드시 포함하는 리스트를 가지고 있는지 확인하기 위해 *를 사용할 수 있습니다:

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        // Assert that each user in the list has at least an id, name and email attribute.
        $this->get('/users')
             ->seeJsonStructure([
                 '*' => [
                     'id', 'name', 'email'
                 ]
             ]);
    }
}

* 표기는 중첩할 수도 있습니다. 다음 예제의 경우 JSON response는 각각의 사용자가 주어진 속성들을 가지고 사용자의 pet들은 중첩되어 주어진 속성들을 가지고 있는지 확인할 수 있습니다:

$this->get('/users')
     ->seeJsonStructure([
         '*' => [
             'id', 'name', 'email', 'pets' => [
                 '*' => [
                     'name', 'age'
                 ]
             ]
         ]
     ]);

세션 / 인증

라라벨은 테스팅 중 세션 작업을 하는 데 필요한 여러 헬퍼들을 제공합니다. 먼저, withSession 메소드를 이용하여 주어진 배열을 세션 데이터로 설정할 수 있습니다. 이것은 애플리케이션에 response-응답을 전달하기 전에 데이터를 세션에 로드하는 경우에 유용합니다:

<?php

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $this->withSession(['foo' => 'bar'])
             ->visit('/');
    }
}

물론 일반적인 세션의 이용법 중 하나는 인증된 사용자를 위해서 상태를 유지하는 것입니다. actingAs 헬퍼 메소드는 특정 사용자를 현재 사용자로 인증하는 단순한 방법을 제공합니다. 예를 들어, 사용자를 생성하고 인증하기 위해 model factory를 사용할 수 있습니다:

<?php

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $user = factory(App\User::class)->create();

        $this->actingAs($user)
             ->withSession(['foo' => 'bar'])
             ->visit('/')
             ->see('Hello, '.$user->name);
    }
}

또한 actingAs 메소드의 두 번째 인자로 주어진 사용자에 대한 인증에 어떤 guard를 사용해야 하는지 지정하도록 guard 이름을 전달 할 수도 있습니다:

$this->actingAs($user, 'api')

미들웨어 비활성화 시키기

애플리케이션을 테스트할 때 일부 테스트에서 미들웨어를 비활성화 하는 편리한 방법을 원할 수도 있습니다. 이는 어떤 미들웨어에도 관계 없이 라우트와 컨트롤러를 테스트할 수 있게 해줍니다. 라라벨은 자동으로 테스트 클래스의 모든 미들웨어를 비활성화할 수 있는 간단한 WithoutMiddleware 트레이트-trait을 포함하고 있습니다:

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use WithoutMiddleware;

    //
}

몇 개의 테스트 메소드에서만 미들웨어를 비활성화하기를 원한다면 테스트 메소드에서 withoutMiddleware 메소드를 호출하면 됩니다:

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->withoutMiddleware();

        $this->visit('/')
             ->see('Laravel 5');
    }
}

사용자 정의 HTTP 요청

애플리케이션에 사용자 정의 HTTP request-요청을 하고 Illuminate\Http\Response 객체 전체를 가져오려면 call 메소드를 이용하면 됩니다:

public function testApplication()
{
    $response = $this->call('GET', '/');

    $this->assertEquals(200, $response->status());
}

POST, PUT, 혹은 PATCH 요청의 경우, 해당 요청과 함께 입력 데이터의 배열을 전달할 수 있습니다. 물론 이 데이터는 request 인스턴스를 통해 라우트와 컨트롤러에서 사용 가능할 수 있습니다:

   $response = $this->call('POST', '/user', ['name' => 'Taylor']);

PHPUnit Assertions

라라벨은 PHPUnit 테스트를 위해 다양한 커스텀 assertion 메소드를 제공합니다.

메소드 설명
->assertResponseOk(); 클라이언트의 응답이 괜찮은(OK) 상태 코드를 갖고 있는지 확인.
->assertResponseStatus($code); 클라이언트 응답이 주어진 코드를 가지고 있는지 확인.
->assertViewHas($key, $value = null); 응답 뷰가 주어진 데이터의 특정 부분을 가지고 있는지 확인.
->assertViewHasAll(array $bindings); 뷰가 주어진 데이터의 특정 목록을 가지고 있는지 확인.
->assertViewMissing($key); 응답 뷰가 주어진 데이터의 특정 부분을 가지고 있지 않는지 확인.
->assertRedirectedTo($uri, $with = []); 클라이언트가 특정 URI로 리다이렉트되었는지 여부를 확인.
->assertRedirectedToRoute($name, $parameters = [], $with = []); 클라이언트가 특정 라우트로 리다이렉트되었는지 확인.
->assertRedirectedToAction($name, $parameters = [], $with = []); 클라이언트가 특정 동작으로 리다이렉트되었는지 확인.
->assertSessionHas($key, $value = null); 세션이 주어진 값을 가지는지 확인.
->assertSessionHasAll(array $bindings); 세션이 주어진 값의 목록을 가지는지 확인.
->assertSessionHasErrors($bindings = [], $format = null); 세션이 주어진 오류들을 가지고 있는지 확인.
->assertHasOldInput(); 세션이 이전의 입력값을 가지고 있는지 확인.
->assertSessionMissing($key); 세션이 주어진 키를 가지고 있지 않는 것을 확인.