在laravel 5.1单元测试中嘲笑hasMany关系

Posted

技术标签:

【中文标题】在laravel 5.1单元测试中嘲笑hasMany关系【英文标题】:mocking hasMany relation in laravel 5.1 unit test 【发布时间】:2018-03-14 20:37:15 【问题描述】:

对于 laravel 5.1 中的单元测试,我正在尝试测试客户端模型的级联删除功能,该功能设置了递归标志,还应该删除与客户端关联的所有用户。 我想使用模拟用户并仅在调用用户的删除功能时进行测试,因此我不必使用数据库,并且将来将相同的原理应用于其他测试。

目前测试失败,因为我无法找到一种方法让客户端模型在不触发查询的情况下检索关联用户。 我想我需要模拟客户端的 hasMany 关系定义函数,但我还没有找到方法。

客户端模型:

class Client extends Model




    protected $table = 'clients';

    protected $fillable = [];



    public function casDelete($recursive = false)
        if($recursive) 
            $users = $this->users()->get();
            foreach($users as $user) 
                $user->casDelete($recursive);
            
        
        $this->delete();
    

    public function users()
        return $this->hasMany('App\User');
    

用户模型:

class User extends Model implements AuthenticatableContract, CanResetPasswordContract

    use Authenticatable, CanResetPassword;

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name', 'email', 'password', 'client_id'];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = ['password', 'remember_token'];

    public function casDelete($recursive = false)
        $this->delete();
    

    public function client()
        return $this->belongsTo('App\Client');
    


测试:

class ClientModelTest extends TestCase

    use DatabaseTransactions;

    function testCasDelete()
    $client = factory(Client::class)->create();
    $user = factory(User::class)->make(['client_id' => $client->id]);

    $observer = $this->getMock('user');
    $observer->expects($this->once())->method('casDelete');

    $client->casDelete(true);


    

【问题讨论】:

不能使用工厂制作User,需要给$client关联一个mock用户。 @apokryfos 好的,如何做到这一点?我试过\Mockery::mock('Client')->shouldReceive('users')->andReturns($mockedUser)之类的东西,但不会返回给实际测试的客户端 $users = $this->users()->get(); 无论如何都会运行数据库查询。您还需要模拟客户端,特别是 users() 方法应该返回另一个模拟,其 get() 方法返回模拟用户。 【参考方案1】:

当您使用 DatabaseTransactions 时,这意味着您希望将数据持久保存在数据库中。当您使用工厂的 create() 时,您仍在使用数据库,因此您根本不应该使用数据库,或者如果您希望使用数据库,那么您可以简单地解决问题。但我可以建议的是这个解决方案,我没有使用数据库初始化。

    $user = \Mockery::mock();
    $user->shouldReceive('casDelete')->andReturnNull();

    $queryMock = \Mockery::mock();
    $queryMock->shouldReceive('get')->andReturn([$user]);

    $clientMock = \Mockery::mock(Client::class)->makePartial();
    $clientMock->shouldReceive('users')->andreturn($queryMock);
    $clientMock->casDelete(true);

这样您就可以确定您已在每个用户模型上调用了 casDelete。 这是一个非常简单的测试用例,您可以根据您想要实现的目标以您喜欢的方式对其进行扩展。

【讨论】:

以上是关于在laravel 5.1单元测试中嘲笑hasMany关系的主要内容,如果未能解决你的问题,请参考以下文章

如果在Laravel 5.5单元测试中隐藏密码,如何模拟用户创建

嘲笑 Laravel 雄辩的水合关系

Laravel 5.1 使用 JSON 的 API 的 PHPUnit 测试

Android单元测试没有被嘲笑

函数在C中嘲笑?

Laravel 5.1通过急切加载返回连接字段