우아한 PHP Test Framework Pest - #2 Test 작성



Pest 는 단위 테스트를 간략하게 작성할 수 있도록 도와주며 laravel 의 test 폴더 구조에 맞게 Unit 과 Feature 에 있는 Test.php 로 끝나는 모든 클래스를 자동으로 실행합니다.

tests
    - Unit
		- ComponentTest.php -- 유닛 테스트 클래스
    - Feature
		- HomeTest.php      -- 피처 테스트 클래스
	Pest.php				-- Pest 부트스트랩
phpunit.xml


이제 실제 테스트 케이스 작성을 위해 다음 artisan 명령으로 테스트 클래스를 생성합니다.

Unit Test class 생성
$ php artisan make:test ComponentTest --unit
Feature Test class 생성
$ php artisan make:test HomeTest


Pest 를 사용하면 기존의 TestCase 를 상속받지 않고 다음과 같이 test() 나 it() 메서드로 간략하게 테스트를 작성할 수 있습니다.

HomeTest.php
<?php

test('has home', function () {
    $this->assertTrue(true);
});

// 또는
it('has home', function () {
    $this->assertTrue(true);
});


테스트 함수 작성 - test() 와 it()

test() 함수의 첫 번째 파라미터는 test 에 대한 설명, 2번째는 클로저입니다.

<?php

function test(string $description = null, Closure $closure = null)
{


it() 도 마찬가지이며 차이점은 $description 앞에 "it" 이라는 단어를 추가해서 실행한다는 점입니다.

<?php

function it(string $description = null, Closure $closure = null)
{
HomeTest.php
<?php

test('has home', function () {
    $this->assertTrue(true);
});

// 또는
it('has home', function () {
    $this->assertTrue(true);
});


HomeTest.php 에 작성한 테스트를 실행하면 다음과 같은 결과를 표시하며 it() 의 경우 description 에 it 이라는 문자열을 추가한 것을 확인할 수 있습니다.

./vendor/bin/pest


테스트 케이스에 바인딩 - uses()

테스트 케이스에서 특정 클래스나 trait 이 필요할 수 있습니다. 예로 Database 테스트가 끝난 후에  RefreshDatabase trait 을 사용해서 테스트 데이타를 지워야 합니다.

이럴 경우 클래스나 trait 을 현재 테스트 케이스에 바인딩 해주는 uses() 함수를 사용하면 됩니다.


HomeTest.php
<?php

use App\Models\User;
use \Illuminate\Foundation\Testing\WithFaker;
use \Illuminate\Foundation\Testing\RefreshDatabase;

// 테스크 케이스에 trait 바인딩
uses(WithFaker::class);
uses(RefreshDatabase::class);

test('has user', function () {
    $faker = $this->faker('ko_KR');

    $u = User::create([
       'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt('secret'),
    ]);

    $this->assertDatabaseHas('users', [
        'email' => $u->email,
    ]);
});


이제 pest 를 실행하면 단위 테스트를 실행하고 테스트 데이타를 삭제한 것을 확인할 수 있습니다.

./vendor/bin/pest

   PASS  Tests\Unit\ComponentTest
  ✓ example

   PASS  Tests\Unit\ExampleTest
  ✓ basic test

   PASS  Tests\Feature\ExampleTest
  ✓ basic test

   PASS  Tests\Feature\HomeTest
  ✓ has user

  Tests:  4 passed
  Time:   1.16s


하위 폴더에도 바인딩 - in()

RefreshDatabase 처럼 여러 테스트 케이스에서 사용할 trait 을 매번 지정하는 건 번거로운 일입니다. 이런 불편을 해결하려면 Pest 의 Boot strap 파일인 Pest.php 에 uses() 함수를 옮기면 됩니다.

Pest.php
<?php

uses(Tests\TestCase::class)->in('Feature');

// 테스크 케이스에 trait 바인딩
uses(\Illuminate\Foundation\Testing\WithFaker::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
HomeTest.php
<?php

use App\Models\User;

test('has user', function () {
    $faker = $this->faker('ko_KR');

    $u = User::create([
       'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt('secret'),
    ]);

    $this->assertDatabaseHas('users', [
        'email' => $u->email,
    ]);
});


이제 Pest 를 실행하면 다음과 같이 에러가 발생합니다.

./vendor/bin/pest


이는 Feature 는 하위 폴더이므로 uses() 로 바인딩해도 영향을 주지 않아서이며 in() 함수로 하위 폴더까지 바인딩하도록 선언해야 합니다.


Pest.php 를 아래와 같이 in() 을 호출하도록 수정하고 다시 실행하면 정상 동작하는 것을 확인할 수 있습니다.

Pest.php
<?php

uses(Tests\TestCase::class)->in('Feature');

// 테스크 케이스에 trait 바인딩
uses(\Illuminate\Foundation\Testing\WithFaker::class)->in('Feature');
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');


만약 Unit 하위 폴더에도 바인딩할 경우 in() 을 별도로 호출해 주면 됩니다.

Pest.php
<?php

uses(Tests\TestCase::class)->in('Feature');

// 테스크 케이스에 trait 바인딩
uses(\Illuminate\Foundation\Testing\WithFaker::class)->in('Feature');
uses(\Illuminate\Foundation\Testing\WithFaker::class)->in('Unit');
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Unit');


또는 uses() 의 첫 번째 함수에 바인딩할 trait 이름을 배열로 넘기면 더 깔끔하게 작성할 수 있습니다.

Pest.php
<?php

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;

uses(Tests\TestCase::class)->in('Feature');

// 테스크 케이스에 trait 바인딩
uses(WithFaker::class, RefreshDatabase::class)->in('Feature');
uses(WithFaker::class, RefreshDatabase::class)->in('Unit');

Ref