如何测试 Laravel 5.4 中的“创建”事件是不是调用了 Eloquent 模型方法?

Posted

技术标签:

【中文标题】如何测试 Laravel 5.4 中的“创建”事件是不是调用了 Eloquent 模型方法?【英文标题】:How can I test whether Eloquent model method is called by "creating" event in Laravel 5.4?如何测试 Laravel 5.4 中的“创建”事件是否调用了 Eloquent 模型方法? 【发布时间】:2017-12-15 14:12:00 【问题描述】:

我想测试当eloquent event 被触发时是否调用了一个方法。

在下面的示例中,我想在 Student 实例保存到数据库。

这是Student模型类:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Student extends Model


    public $guarded = [];


    protected static function boot()
    
        parent::boot();

        // set approved attribute if its value is NULL
        self::creating(function (self $student) 
            $student->approved = $student->approved ?? $student->isApproved();
        );
    

    /**
     * @return bool
     */
    public function isApproved()
    
        return ($this->age >= 14) && ($this->age <= 20);
    

为了实现这一点,我在 Student 类的 creating 事件上附加了一个回调函数。

我正在尝试使用以下测试来测试 isApproved() 方法调用:

<?php

namespace Tests\Feature;

use App\Student;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class StudentTest extends TestCase


    use DatabaseMigrations;

    /**
     * @test
     */
    public function student_is_auto_approved_on_save()
    

        $mock = \Mockery::mock(Student::class);
        $mock->shouldReceive('isApproved')->once();

        Student::create([
            'name' => 'John Doe',
            'age' => 14
        ]);
    

但测试未通过,显示以下转储

    PHPUnit 5.7.21 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 246 ms, Memory: 16.00MB

There was 1 error:

1) Tests\Feature\StudentTest::student_is_auto_approved_on_save
Mockery\Exception\InvalidCountException: Method isApproved() from Mockery_0_App_Student should be called
 exactly 1 times but called 0 times.

/students/vendor/mockery/mockery/library/Mockery/CountValidator/Exact.php:37
/students/vendor/mockery/mockery/library/Mockery/Expectation.php:298
/students/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120
/students/vendor/mockery/mockery/library/Mockery/Container.php:297
/students/vendor/mockery/mockery/library/Mockery/Container.php:282
/students/vendor/mockery/mockery/library/Mockery.php:152
/students/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:144
/home/user/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:186
/home/user/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:116

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

我做错了什么?

【问题讨论】:

【参考方案1】:

试试

<?php

namespace Tests\Feature;

use App\Student;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class StudentTest extends TestCase

    use DatabaseMigrations;

    /**
     * @test
     * @dataProvider providerAutoApprovedAge
     * 
     * @param int $age
     */
    public function student_is_auto_approved_on_save($age)
    
        $student = Student::create([
            'name' => 'John Doe' . $age,
            'age' => $age,
        ]);

        $student->save();

        $this->assertTrue($student->approved);
    

    public function providerAutoApprovedAge()
    
        for ($age = 14; $age <= 20; $age++) 
            yield [
                $age,
            ];
        
    

    /**
     * @test
     * @dataProvider providerNotAutoApprovedAge
     * 
     * @param int $age
     */
    public function student_is_not_auto_approved_on_save($age)
    
        $student = Student::create([
            'name' => 'John Doe' . $age,
            'age' => $age,
        ]);

        $student->save();

        $this->assertFalse($student->approved);
    

    public function providerNotAutoApprovedAge()
    
        for ($age = 0; $age < 14; $age++) 
            yield [
                $age,
            ];
        

        for ($age = 21; $age < 120; $age++) 
            yield [
                $age,
            ];
        
    

您可能想要断言该学生是

自动批准所需年龄范围内的年龄 对于超出规定年龄范围的年龄不会自动获得批准

您可以为此使用数据提供程序。

或者,您可以像这样将方法存根:

class StudentStub extends Student

    public $hasApprovedBeenInvoked = false;

    public function isApproved()
    
        $this->hasApprovedBeenInvoked = true;
    


class StudentTest extends TestCase

    use DatabaseMigrations;

    /**
     * @test
     */
    public function student_is_auto_approved_on_save($age)
    
        $student = StudentStub::create([
            'name' => 'John Doe' . $age,
            'age' => $age,
        ]);

        $student->save();

        $this->assertTrue($student->hasApprovedBeenInvoked);
    

但是,这仅在Student::create() 使用后期静态绑定并返回StutentStub 的实例时才有效。

大多数情况下,您不想模拟被测系统,而是模拟合作者(如果有的话)。这里,没有。

参考,见

https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers https://laravel.com/docs/5.4/eloquent#events http://php.net/manual/en/language.oop5.late-static-bindings.php

【讨论】:

感谢您的回答。这里的想法是我想测试这部分 self::creating(function (self $student) $student->approved = $student->approved ?? $student->isApproved(); ); 如果我想测试方法 isApproved() 本身,答案是有效的。我已经测试过了。我想确保在保存模型时自动调用该方法。 @user1878906 更新了测试,现在调用save() 我想通过测试完成的唯一想法是确保在 save() 上调用方法 isApproved()。我不是专家,但我觉得这不是测试此调用的 副作用 的最佳方法 - 在本例中是 $student-&gt;approved 属性的值。该示例过于简化,但以下问题的答案会有所帮助应编写什么测试以确保在 save() 上调用方法 isApproved() @user1878906 你可以把方法删掉,让我调整一下例子。

以上是关于如何测试 Laravel 5.4 中的“创建”事件是不是调用了 Eloquent 模型方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Laravel 5.4中使用ask()测试工匠命令

如何将 Laravel 5.4 与 Angular 4 集成

使用迁移 Laravel 5.4 更新列

脚本 php artisan 优化处理使用 laravel 5.4 返回的 post-update-cmd 事件,错误代码为 1

php artisan migrate 不适用于 Laravel 5.4 中的 XAMPP

Laravel 5.4 - 在已经登录的情况下创建另一个用户帐户