【问题标题】:Laravel preparing the database before a test for a model with many dependencies, is there a better way?Laravel 在测试具有许多依赖项的模型之前准备数据库,有没有更好的方法?
【发布时间】:2017-01-23 09:05:25
【问题描述】:

我将 Laravel 5.2 用于我的 Web 应用程序和扩展 PHPUnit 的捆绑测试工具。

我正在为对模型执行更新的页面编写测试。测试在提交表单之前检查模型的属性之一是否未更改,以防止两个用户同时在浏览器中打开模型时更新模型。

测试需要在我的 Laravel 网络应用程序中使用一个模型,即App\Models\Application,我正在使用工厂为这个模型创建一些假数据。 Application 模型有许多 belongsTo 关系,这反过来要求在创建 Application 模型之前使用工厂在测试中创建相关模型。

例如,Application 模型与Applicant 具有belongsTo 关系,因此我需要在创建Application 之前使用工厂创建Applicant 模型,并且Applicant 也具有@ 987654331@ 与 User 的关系,我还需要在此之前创建它......你明白了。

因此,简而言之,由于我要测试的一个模型的关系依赖关系,我正在创建几个模型实例。

所以这让我想知道我是否采取了错误的方法?有没有更简单的方法,我只创建正在测试的模型?

这是我的测试代码:

/** 
 * @test
 */
public function throw_exception_when_application_status_is_modified_after_submission()
{
    /**
     * Arrange
     */
    // create roles
    factory(Role::class, 'admin')->create();
    factory(Role::class, 'applicant')->create();

    // create admin user
    $adminUser = factory(User::class)->create();
    $adminUser->attachRole(Role::whereName('admin')->first());
    // create applicant user
    $applicantUser = factory(User::class)->create();
    $applicantUser->attachRole(Role::whereName('applicant')->first());

    // create organisation with type
    $organisationType = factory(OrganisationType::class)->create();
    $organisation = factory(Organisation::class)->create();
    $organisation->organisationTypes()->attach($organisation->id);

    // create sub application
    $subApplication = factory(ExperienceLetter::class)->create();

    // create applicant
    $applicant = factory(Applicant::class)->create([
        'user_id' => $applicantUser->id
    ]);

    // create application
    $application = factory(Application::class)->make([
        'status' => 'pending',
        'application_id' => $subApplication->id,
        'application_type' => 'App\Models\ExperienceLetter',
        'organisation_id' => $organisation->id,
        'applicant_id' => $applicant->id,
    ]);    

    // prep data
    $data = [
        'action' => 'accept',
        'details' => 'email sent',
        'application_id' => $application->id,
        'application_status' => 'pending',
        'verification' => 'email',
    ];

    /**
     * Act
     */
    // log in
    $this->actingAs($adminUser);

    // alter status before call()
    $application->status = 'verification';
    $application->save();

    $response = $this->call('POST', route('admin.application.action.store'), $data);

    /**
     * Assert
     */
    $this->assertEquals(302, $response->status());
    // assertSessionHasErrors
}

感谢您的建议。

【问题讨论】:

    标签: php laravel testing phpunit


    【解决方案1】:

    您是否因为外键限制而创建所有这些?如果您只想检查一个没有任何关系的模型,那么Schema::disableForeignKeyConstraints(); 是您的朋友:

    /** 
     * @test
     */
    public function throw_exception_when_application_status_is_modified_after_submission()
    {
        \Schema::disableForeignKeyConstraints();
        /**
         * Arrange
         */
        // create admin user
        $adminUser = factory(User::class)->create();
        $adminUser->attachRole(factory(Role::class, 'admin')->create());
    
        // create application
        $application = factory(Application::class)->make([
            'status' => 'pending',
            'application_id' => 0,
            'application_type' => 'App\Models\ExperienceLetter',
            'organisation_id' => 0,
            'applicant_id' => 0,
        ]);    
    
        // prep data
        $data = [
            'action' => 'accept',
            'details' => 'email sent',
            'application_id' => $application->id,
            'application_status' => 'pending',
            'verification' => 'email',
        ];
    
        /**
         * Act
         */
        // log in
        $this->actingAs($adminUser);
    
        // alter status before call()
        $application->status = 'verification';
        $application->save();
    
        $response = $this->call('POST', route('admin.application.action.store'), $data);
    
        \Schema::enableForeignKeyConstraints();
        /**
         * Assert
         */
        $this->assertEquals(302, $response->status());
        // assertSessionHasErrors
    }
    

    【讨论】:

    • 嗨,埃里克。感谢您的答复。原来是这个原因!我很快就会试一试,看看效果如何。这适用于 MySQL 测试数据库和内存测试数据库中的 SQLite 吗?
    • 我目前使用的是 Laravel 5.2,在代码库中似乎找不到这个方法。运行我的测试时出现此错误Error: Call to undefined method Illuminate\Database\Schema\MySqlBuilder::disableForeignKeyConstraints()。也尝试过\DB::getSchemaBuilder()->disableForeignKeyConstraints();,结果相同。有什么想法吗?
    • API 文档状态存在于 5.2 中:laravel.com/api/5.2/Illuminate/Database/Schema/Builder.html 但是当我在我的代码库中查看vendor\laravel\framework\src\Illuminate\Database\Schema\Builder 时,我找不到该方法。也许我需要更新 Laravel 的次要版本?
    • Running composer update laravel/framework 更新了次要版本,似乎已经成功了。 Removing laravel/framework (v5.2.31) Installing laravel/framework (v5.2.45) 测试现在通过了\Schema::disableForeignKeyConstraints();,正如你所建议的那样。
    • 我不会说这是不好的做法,只要您始终如一。你只想确保如果你在测试中省略了某些东西或模拟(如果你正在使用模拟)和对象,那是因为你已经对那个东西进行了测试,以确保你不会忽略一些在现场会失败的东西在达到您正在测试的场景之前的环境。除此之外,添加更多测试绝不是一个坏主意,尤其是对于像您这样的边缘情况。
    猜你喜欢
    • 2014-01-09
    • 1970-01-01
    • 2016-07-24
    • 2014-07-25
    • 2020-05-15
    • 2019-08-19
    • 2021-07-05
    • 1970-01-01
    相关资源
    最近更新 更多