테스트 데이터 생성

애플리케이션의 테스트를 실행하기 위해 샘플 데이터가 필요한 경우가 많습니다. Fabricator클래스는 Faker를 사용하여 모델을 임의 데이터 생성기로 변환합니다. 시드(seed) 또는 테스트 케이스에서 패브리케이터(Fabricator)를 사용하여 단위 테스트를 위한 가짜 데이터를 준비하십시오.

지원되는 모델

Fabricator는 프레임워크의 핵심 모델인 CodeIgniter\Model을 상속하는 모든 모델을 지원합니다. CodeIgniter\Test\Interfaces\FabricatorModel을 구현한 커스텀 모델을 사용할 수도 있습니다:

<?php

namespace App\Models;

use CodeIgniter\Test\Interfaces\FabricatorModel;

class MyModel implements FabricatorModel
{
    public function find($id = null)
    {
        // TODO: Implement find() method.
    }

    public function insert($row = null, bool $returnID = true)
    {
        // TODO: Implement insert() method.
    }

    // ...
}

참고

메서드 외에도, 인터페이스는 대상 모델에 필요한 몇 가지 속성을 정의합니다. 자세한 내용은 인터페이스 코드를 참고하십시오.

패브리케이터 로드

가장 기본적인 형태에서, 패브리케이터는 작동할 모델을 인자로 받습니다:

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator(UserModel::class);

매개변수는 모델의 이름을 지정하는 문자열이거나 모델 인스턴스 자체일 수 있습니다:

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$model = new UserModel($testDbConnection);

$fabricator = new Fabricator($model);

포매터 정의

Faker는 포매터로부터 데이터를 요청하여 생성합니다. 포매터가 정의되지 않은 경우, Fabricator는 필드 이름과 모델 속성을 기반으로 가장 적절한 포매터를 추측하며, $fabricator->defaultFormatter로 폴백됩니다. 필드 이름이 일반적인 포매터와 일치하거나 필드 내용이 크게 중요하지 않다면 이 방법도 괜찮지만, 대부분의 경우 생성자의 두 번째 매개변수로 사용할 포매터를 직접 지정하는 것이 좋습니다:

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$formatters = [
    'first'  => 'firstName',
    'email'  => 'email',
    'phone'  => 'phoneNumber',
    'avatar' => 'imageUrl',
];

$fabricator = new Fabricator(UserModel::class, $formatters);

패브리케이터가 초기화된 후에도 setFormatters() 메서드를 사용하여 포매터를 변경할 수 있습니다.

고급 포맷팅

포매터의 기본 반환값으로 충분하지 않을 때도 있습니다. Faker 프로바이더는 대부분의 포매터에 매개변수를 허용하여 임의 데이터의 범위를 더 제한할 수 있습니다. 패브리케이터는 모델에서 fake() 메서드를 찾아, 그 안에서 가짜 데이터의 형태를 정확하게 정의할 수 있습니다:

<?php

namespace App\Models;

use Faker\Generator;

class UserModel
{
    // ...

    public function fake(Generator &$faker)
    {
        return [
            'first'  => $faker->firstName(),
            'email'  => $faker->email(),
            'phone'  => $faker->phoneNumber(),
            'avatar' => \Faker\Provider\Image::imageUrl(800, 400),
            'login'  => config('Auth')->allowRemembering ? date('Y-m-d') : null,
        ];

        /*
         * Or you can return a return type object.

        return new User([
            'first'  => $faker->firstName(),
            'email'  => $faker->email(),
            'phone'  => $faker->phoneNumber(),
            'avatar' => \Faker\Provider\Image::imageUrl(800, 400),
            'login'  => config('Auth')->allowRemembering ? date('Y-m-d') : null,
        ]);

        */
    }
}

이 예시에서 처음 세 값은 이전의 포매터와 동일하다는 점에 주목하십시오. 그러나 avatar의 경우 기본값이 아닌 다른 이미지 크기를 요청했고, login은 앱 설정에 따른 조건문을 사용했는데, 이 두 가지는 $formatters 매개변수로는 불가능한 것들입니다.

테스트 데이터를 프로덕션 모델과 분리하고 싶을 수 있으므로, 테스트 지원 폴더에 자식 클래스를 정의하는 것이 좋은 방법입니다:

<?php

namespace Tests\Support\Models;

use App\Models\UserModel;
use Faker\Generator;

class UserFabricator extends UserModel
{
    public function fake(Generator &$faker)
    {
        // ...
    }
}

수정자 설정

Added in version 4.5.0.

Faker는 모든 프로바이더 호출 전에 사용하는 세 가지 특별한 프로바이더인 unique(), optional(), valid()를 제공합니다. 패브리케이터는 전용 메서드를 통해 이러한 수정자를 완전히 지원합니다.

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator(UserModel::class);
$fabricator->setUnique('email'); // sets generated emails to be always unique
$fabricator->setOptional('group_id'); // sets group id to be optional, with 50% chance to be `null`
$fabricator->setValid('age', static fn (int $age): bool => $age >= 18); // sets age to be 18 and above only

$users = $fabricator->make(10);

필드 이름 다음에 전달된 인수는 수정자에게 그대로 전달됩니다. 자세한 내용은 Faker의 수정자 문서를 참고하십시오.

패브리케이터(Fabricator)의 각 메서드를 호출하는 대신, 모델에서 fake() 메서드를 사용하는 경우 Faker의 수정자를 직접 사용할 수도 있습니다.

<?php

namespace App\Models;

use CodeIgniter\Test\Fabricator;
use Faker\Generator;

class UserModel
{
    protected $table = 'users';

    public function fake(Generator &$faker)
    {
        return [
            'first'    => $faker->firstName(),
            'email'    => $faker->unique()->email(),
            'group_id' => $faker->optional()->passthrough(mt_rand(1, Fabricator::getCount('groups'))),
        ];
    }
}

로컬라이제이션

Faker는 다양한 로케일을 지원합니다. 어떤 프로바이더가 원하는 로케일을 지원하는지 확인하려면 해당 문서를 참고하십시오. 패브리케이터를 초기화할 때 세 번째 매개변수에 로케일을 지정합니다:

<?php

use App\Models\UserModel;
use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator(UserModel::class, null, 'fr_FR');

로케일을 지정하지 않으면 app/Config/App.php에서 defaultLocale로 정의된 로케일을 사용합니다. getLocale() 메서드를 통해 기존 패브리케이터의 로케일을 확인할 수 있습니다.

데이터 위조

패브리케이터가 올바르게 초기화되면 make() 명령으로 테스트 데이터를 쉽게 생성할 수 있습니다:

<?php

use CodeIgniter\Test\Fabricator;
use Tests\Support\Models\UserFabricator;

$fabricator = new Fabricator(UserFabricator::class);
$testUser   = $fabricator->make();
print_r($testUser);

다음과 같은 결과를 얻을 수 있습니다:

<?php

[
    'first'  => 'Maynard',
    'email'  => 'king.alford@example.org',
    'phone'  => '201-886-0269 x3767',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

개수를 지정하여 여러 개를 한 번에 얻을 수도 있습니다:

<?php

$users = $fabricator->make(10);

make()의 반환 타입은 해당 모델에 정의된 타입을 따르지만, 메서드를 직접 사용하여 타입을 강제 지정할 수도 있습니다:

<?php

$userArray  = $fabricator->makeArray();
$userObject = $fabricator->makeObject();
$userEntity = $fabricator->makeObject('App\Entities\User');

make()의 반환값은 테스트에서 사용하거나 데이터베이스에 삽입할 준비가 된 상태입니다. 또는 Fabricator에 포함된 create() 명령을 사용하면 데이터베이스에 자동으로 삽입하고 결과를 반환합니다. 모델 콜백, 데이터베이스 포맷팅, 기본 키 및 타임스탬프와 같은 특수 키로 인해 create()의 반환값은 make()와 다를 수 있습니다. 다음과 같은 결과를 얻을 수 있습니다:

<?php

[
    'id'         => 1,
    'first'      => 'Rachel',
    'email'      => 'bradley72@gmail.com',
    'phone'      => '741-241-2356',
    'avatar'     => 'http://lorempixel.com/800/400/',
    'login'      => null,
    'created_at' => '2020-05-08 14:52:10',
    'updated_at' => '2020-05-08 14:52:10',
];

make()와 마찬가지로 개수를 지정하여 객체 배열을 삽입하고 반환받을 수 있습니다:

<?php

$users = $fabricator->create(100);

마지막으로, 실제 데이터베이스를 사용하지 않으면서도 완전한 데이터베이스 객체로 테스트하고 싶은 경우가 있습니다. create()는 두 번째 매개변수를 통해 객체 모킹을 허용하며, 데이터베이스에 실제로 접근하지 않고 위에서 설명한 추가 데이터베이스 필드가 포함된 객체를 반환합니다:

<?php

$user = $fabricator(null, true);

$this->assertIsNumeric($user->id);
$this->dontSeeInDatabase('user', ['id' => $user->id]);

테스트 데이터 지정

자동 생성된 데이터도 좋지만, 포매터 설정을 변경하지 않고 특정 필드에 고정 값을 지정하고 싶은 경우가 있습니다. 각 변형마다 새 패브리케이터를 만드는 대신 setOverrides()를 사용하여 원하는 필드의 값을 지정할 수 있습니다:

<?php

$fabricator->setOverrides(['first' => 'Bobby']);
$bobbyUser = $fabricator->make();

이제 make() 또는 create()로 생성된 모든 데이터의 first 필드에는 항상 “Bobby”가 사용됩니다:

<?php

[
    'first'  => 'Bobby',
    'email'  => 'latta.kindel@company.org',
    'phone'  => '251-806-2169',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

[
    'first'  => 'Bobby',
    'email'  => 'melissa.strike@fabricon.us',
    'phone'  => '525-214-2656 x23546',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

setOverrides()는 두 번째 매개변수를 통해 재정의가 영구적으로 유지될지, 단일 동작에만 적용될지를 지정할 수 있습니다:

<?php

$fabricator->setOverrides(['first' => 'Bobby'], $persist = false);
$bobbyUser = $fabricator->make();
$bobbyUser = $fabricator->make();

첫 번째 반환 후에는 패브리케이터가 재정의를 사용하지 않게 되는 것을 확인하십시오:

<?php

[
    'first'  => 'Bobby',
    'email'  => 'belingadon142@example.org',
    'phone'  => '741-857-1933 x1351',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

[
    'first'  => 'Hans',
    'email'  => 'hoppifur@metraxalon.com',
    'phone'  => '487-235-7006',
    'avatar' => 'http://lorempixel.com/800/400/',
    'login'  => null,
];

두 번째 매개변수를 지정하지 않으면 전달된 값은 기본적으로 영구 유지됩니다.

테스트 헬퍼

테스트를 위해 일회성 가짜 객체만 필요한 경우가 많습니다. 테스트 헬퍼는 이를 위해 fake($model, $overrides, $persist = true) 함수를 제공합니다:

<?php

helper('test');
$user = fake('App\Models\UserModel', ['name' => 'Gerry']);

이것은 다음과 동일합니다:

<?php

use CodeIgniter\Test\Fabricator;

$fabricator = new Fabricator('App\Models\UserModel');
$fabricator->setOverrides(['name' => 'Gerry']);
$user = $fabricator->create();

데이터베이스에 저장하지 않고 가짜 객체만 필요한 경우, persist 매개변수에 false를 전달하면 됩니다.

테이블 카운트

가짜 데이터가 다른 가짜 데이터에 의존하는 경우가 자주 있습니다. Fabricator는 각 테이블에 생성된 가짜 항목 수에 대한 정적 카운트를 제공합니다. 다음 예시를 살펴보십시오:

프로젝트에 사용자와 그룹이 있다고 가정합니다. 테스트 케이스에서 다양한 크기의 그룹 시나리오를 만들기 위해 Fabricator를 사용하여 여러 그룹을 생성합니다. 이제 가짜 사용자를 만들고 싶지만 존재하지 않는 그룹 ID에 할당하고 싶지 않은 경우, 모델의 fake 메서드는 다음과 같이 작성할 수 있습니다:

<?php

namespace App\Models;

use CodeIgniter\Test\Fabricator;
use Faker\Generator;

class UserModel
{
    protected $table = 'users';

    public function fake(Generator &$faker)
    {
        return [
            'first'    => $faker->firstName(),
            'email'    => $faker->email(),
            'group_id' => mt_rand(1, Fabricator::getCount('groups')),
        ];
    }
}

이제 새 사용자를 생성하면 유효한 그룹에 속하게 됩니다: $user = fake(UserModel::class);

메서드

Fabricator는 내부적으로 카운트를 관리하지만, 다음 정적 메서드를 통해 직접 접근하여 활용할 수도 있습니다:

getCount(string $table): int

특정 테이블의 현재 카운트 값을 반환합니다 (기본값: 0).

setCount(string $table, int $count): int

특정 테이블의 카운트 값을 수동으로 설정합니다. 예를 들어, 패브리케이터를 사용하지 않고 테스트 항목을 생성했지만 최종 카운트에 포함시키고 싶은 경우에 사용합니다.

upCount(string $table): int

특정 테이블의 카운트 값을 1 증가시키고 새 값을 반환합니다. (이것은 Fabricator::create()에서 내부적으로 사용됩니다.)

downCount(string $table): int

특정 테이블의 카운트 값을 1 감소시키고 새 값을 반환합니다. 예를 들어, 가짜 항목을 삭제했지만 변경 사항을 추적하고 싶은 경우에 사용합니다.

resetCounts()

모든 카운트를 초기화합니다. 테스트 케이스 사이에 이 메서드를 호출하는 것이 좋습니다 (CIUnitTestCase::$refresh = true를 사용하면 자동으로 처리됩니다).