【问题标题】:Laravel - adding relationships to a factory-created modelLaravel - 向工厂创建的模型添加关系
【发布时间】:2020-03-09 19:04:25
【问题描述】:

我正在测试一个包含多对多关系的急切加载关系。现在我在测试中有查询和附件。我想知道是否有办法将它们移入工厂,而不是将其作为测试的一部分。这将限制测试的规模,然后可以在每次创建电影工厂时创建和使用这些关系。

test

public function grabFilmTest()
{
    $film = factory(Film::class)->create();

    $categories = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
    $languages = Languages::where('name', 'english')->first();

    $film->categories()->attach($categories->id);
    $film->languages()->attach($languages->id);

    $response = $this->json('GET', '/film/' . $film->id)
        ->assertStatus(200);

    $response
        ->assertExactJson([
            'id' => $film->id,
            'name' => $film->name,
            'description' => $film->description,
            'categories' => $film->categories->toArray(),
            'languages' => $film->languages->toArray()

}

filmFactory

$factory->define(\App\Models\Film::class, function (Faker $faker){
    return [
        'id' => $faker->uuid,
        'name' => $faker->text,
        'description' => $faker->paragraph,
    ];
});

如果有人可以帮助我如何做到这一点或举个例子,那就太好了:D

【问题讨论】:

    标签: php laravel eloquent phpunit


    【解决方案1】:

    您可以使用factory statesfactory callbacks

    $factory->define(\App\Models\Film::class, function (Faker $faker){
        return [
            'id' => $faker->uuid,
            'name' => $faker->text,
            'description' => $faker->paragraph,
        ];
    });
    $factory->define(\App\Models\Category::class, function (Faker $faker){
        return [
            // Category fields 
        ];
    });
    $factory->define(\App\Models\Language::class, function (Faker $faker){
        return [
            // Language fields 
        ];
    });
    $factory->afterCreatingState(\App\Models\Film::class, 'with-category', function (\App\Models\Film $film) {
        $category = factory(\App\Models\Category::class)->create();
        $film->categories()->attach($category->id);
    });
    $factory->afterCreatingState(\App\Models\Film::class, 'with-language', function (\App\Models\Film $film) {
        $language = factory(\App\Models\Language::class)->create();
        $film->categories()->attach($language->id);
    });
    

    然后你可以在这样的测试中使用:

    public function grabFilmTest()
    {
        $film = factory(Film::class)->create();
        $filmWithCategory = factory(Film::class)->state('with-category')->create();
        $filmWithLanguage = factory(Film::class)->state('with-language')->create();
        $filmWithCategoryAnLanguage = factory(Film::class)->states(['with-category', 'with-language'])->create();
        // ...
    }
    

    PS:我不建议使用现有数据。根据经验,我可以告诉你,这会变得非常痛苦。

    【讨论】:

    • 感谢您提供的示例 :) 您遇到过哪些让您感到痛苦的经历?
    • 我继承了一个大型 Laravel 应用程序(大约有 175 个模型具有关系)。我们使用播种机来填充测试数据库。痛点: 1)播种机维护。当您已经有了基于这些的播种器和测试时,一个小的模式/模型更改将迫使您更改所有播种器或以前的测试。如果你的模型有关系,那么改变就会变得越来越难。 2)在某些情况下,您需要干净的表来进行测试。清理桌子并再次运行播种机(不破坏现有测试)这是一项多余的工作。此外,人际关系会带来更多痛苦。
    【解决方案2】:

    你可以在工厂文件中使用factory callbacks来做:

    <?php
    use \App\Models\Film;
    use \App\Models\Category;
    use \App\Models\Languages;
    
    $factory->define(Film::class, function(Faker $faker){
        return [
            'id'          => $faker->uuid,
            'name'        => $faker->text,
            'description' => $faker->paragraph,
        ];
    });
    
    $factory->afterCreating(Film::class, function(Film $film, Faker $faker) {
        $category = Category::where('main-cat', 'Science')->where('sub-cat', 'Fiction')->first();
        $language = Languages::where('name', 'english')->first();
        $film->categories()->attach($category);
        $film->languages()->attach($language);
    });
    

    【讨论】:

      猜你喜欢
      • 2015-12-09
      • 2017-03-21
      • 2018-06-13
      • 2014-08-17
      • 1970-01-01
      • 2022-11-22
      • 2019-05-28
      • 1970-01-01
      • 2021-11-06
      相关资源
      最近更新 更多