개요

개발 프로젝트를 진행하다 보면 DB 설계 검증, 화면 기획 및 paging test 등의 용도로 테스트 데이타가 필요합니다.

꽤 중요한 기능이지만 테스트 데이타를 만들기 위해 바쁜 시간을 쪼개서 test data 를 만들기는 쉽지 않습니다.


라라벨 모델 팩토리는 의미 있는 테스트 데이타를 만들어 주는 PHP 라이브러리인 fakerphp 를 프레임워크에 연동하여 손쉽게 DB seeding 및 테스트 데이타를 만들수 있는 기능입니다.


laravel 8 부터는 Model Factory 기능이 대폭 변경되었으며 기본적인  사용 방법은 예제인 database/factories/UserFactory.php 에서 볼 수 있습니다.

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = User::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'name' => $this->faker->name(),
            'email' => $this->faker->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

$model property 에 Eloquent model 클래스를 정의하며 definition() 메서드에 만들 model factory 를 지정합니다.

Model Factory 사용

팩토리 생성

artisan 명령어로 사용할 모델 팩토리를 만들 수 있습니다.

php artisan make:factory PostFactory --model Post


database/factories/PostFactory.php 의 definition() 메서드에 다음과 같이 생성할 데이타의 타입에 맞는 지정하면 됩니다.

class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Post::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'author_id' => User::all()->pluck('id')->random(),
            'title' => $this->faker->realText(30),
            'content' => $this->faker->realText(),
            'created_at' => $this->faker->dateTimeBetween('-2 years', 'now'),
        ];
    }
}


factory 사용

factory 메서드를 사용하여 faker 를 호출할 수 있으며 간단하게 tinker 에서 테스트 할 수 있습니다.

데이타 생성

make() 를 사용하면 테스트 데이타를 만들 수 있습니다.

\App\Models\Post::factory()
                ->make();


갯수 지정

count() 메소드를 호출해서 데이타의 갯수를 지정할 수 있습니다.

>>> \App\Models\Post::factory()
  				->count(37)
                ->make(); 

Persistence 데이타 생성

make() 는 임시 데이타만 생성하므로 DB 에 입력하려면 create() 를 사용하면 됩니다.

>>> \App\Models\Post::factory()
  				->count(37)
                ->create();

생성된 데이타는 DBMS 에서 직접 확인할 수 있습니다.

속성 오버라이딩

저자와 책의 정보를 담고 있는 Author와 Book 모델이 있을 경우 Model 의 관계는 다음과 같이 Author 는 여러 개의 Book 을 가질 수 있습니다.


Book 모델 팩토리를 생성할 경우 참조되는 Author 정보가 있어야 합니다. 참조하는 모델 팩토리에서는 다음과 같이 $factory->create() 구문으로 참조되는 모델의 primary key 를 만들 수 있습니다.



또는 아래와 같이 부모 테이블의 id 의 최소/최대 값을 얻어와서 numberBetween($min, $max) 메소드로 구간내의 임의의 값을 설정할 수 있습니다.

$factory->define(App\Book::class, function ($faker) use($factory){
	// 최대값과 최소값 가져오기
	$max = App\Author::max('id');
    $min = App\Author::min('id');
    return [
        'name' => $faker->text,
		// Author id 참조
        'author_id' =>  $faker->numberBetween($min, $max),
        'created_at' => $faker->dateTimeBetween($startDate = '-2 years', $endDate = '-1 years'),
        'updated_at' => $faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now'),
    ];
});

state 사용

state 를 사용하면 Model 의 특별한 상태를 지정해서 생성할 수 있습니다. 만약 Book 모델에 베스트 셀러 여부를 나타내는 is_bestseller 와 주목받는 책임을 나타내는 is_featured 필드가 있다고 가정해 봅시다.


그리고 Model Factory 에 다음과 같이 Model state 를 정의합니다.

$factory->state(App\Book::class, 'bestseller', function() {
    return [
        'is_bestseller' => true,
    ];
});
 
$factory->state(App\Book::class, 'featured', function() {
    return [
        'is_featured' => true,
    ];
});


이제 best seller 인 책을 등록할 경우 아래와 같이 실행하면 is_bestseller 가 true 로 설정됩니다.

factory(\App\Book::class)->states('bestseller')->create();


주목받는 책은 featured state 를 지정해 주면 됩니다.

factory(\App\Book::class)->states('featured')->create();

 동시에 여러 개의 state 를 지정할 경우 파라미터로 구분해서 사용하면 되고 아래는 베스트셀러와 주목받는 책 2가지 state 를 설정합니다.

factory(\App\Book::class)->states('bestseller', 'featured')->create();


factory 에 parameter 전달

TODO

factory 생성시 외부에서 필요한 파라미터를 생성해서 전달할 수 있습니다.  예로 책을 등록할 때 저자 정보를 밖에서 설정해서 입력하고 싶을수 있습니다.

이럴 경우 createmake 메서드에 배열로 전달하려는 파라미터를 key, value 형식으로 입력하면 됩니다.

예로 다음 코드는 author_id => 1 이라는 파라미터를 전달합니다.


사용하는 factory 에서는 다음과 같이 클로저에 array 형식의 parameter 를 기술하고 array 의 값을 확인해서 사용하면 됩니다.

$factory->define(\App\Book::class, function (Faker $faker, array $param) {
   
    return [
		// 저자 정보 외부 입력값 사용
        'author_id' => $param['author_id'] ?? null,
        'title' => $faker->realText(20),
        'comment' => $faker->realText,
    ];
});


Database:Seeding 에서 사용

artisan db:seed 명령어에서도 factory 를 사용할 수 있습니다.

먼저 seed 클래스를 생성합니다.

$ php artisan make:seeder AuthorTableSeeder
Seeder created successfully.


database/seeds/AuthorTableSeeder.php  파일을 수정합니다.

TODO


seed 를 적용합니다.

$ php artisan db:seed


PHPUnit 이나 Controller에서 사용


TODO


Data formatter

TODO


기본 locale 설정하기

Laravel Factory 기본 locale 변경해서 사용하기 참고


같이 보기


Ref