如何测试依赖于具有关系的 Eloquent 模型的类?

Posted

技术标签:

【中文标题】如何测试依赖于具有关系的 Eloquent 模型的类?【英文标题】:How to test a class which depends on an Eloquent model with relationships? 【发布时间】:2014-05-19 13:36:46 【问题描述】:

为依赖于具有关系的 Eloquent 模型的类编写单元测试的最佳方法是什么?例如

真实对象(带数据库)。这很容易,但速度很慢。

真实对象(无数据库)。我可以创建一个新对象,但我看不到如何在不写入数据库的情况下设置相关模型。

模拟对象。我在使用 Mockery 和 Eloquent 模型时遇到了问题(例如,请参阅 this question)。

其他解决方案?

context:我使用 Laravel 和 Authority RBAC 进行访问控制。我想找到在单元测试中测试我的访问规则的最佳方法。这意味着我需要在测试期间将用户依赖项传递给授权。

【问题讨论】:

权宜之计是使用内存中的 SQLite 数据库进行测试。我认为默认情况下 Laravel 实际上会为您执行此操作,尽管您确实需要在设置时调用播种机。不知道它是否真的比 mysql 数据库快(例如),但是当你确实需要一个真正的数据库时,这似乎是一种做法。但是,是的,你真的应该尽可能多地嘲笑。我想要考虑的一件事是,您的测试拥有的代码越多,它们在测试您的实际代码时的实际可靠性就越低,而不是您编写测试以模拟您的“真实”代码的能力。 【参考方案1】:

如果您正在编写单元测试,则永远不应该使用数据库。针对数据库的测试将被视为集成测试。查看 Roy Osherove 的 videos。

为了回答你的问题,(并且没有深入研究权威 RBAC,我会做这样的事情:

// assuming some RBAC class
SomeRBACClass extends RBACBaseClass 

     function validate(UserClass $user) 
         if (!$roles = $user->getRoles())
         
             return false;
         

         $allowed = array('admin', 'superadmin');

         foreach ($roles as $role) 
             if (in_array($role->name, $allowed)) 
                 return true;
             
         

         return false;
     



SomeRBACClassTest extends TestCase 

    function test_validate_WhenPassedUser_callsGetRolesOnUserWithNoArgs()
    
        $rbac = new SomeRBACClass();
        $user = Mockery::mock('UserClass');

        $user->shouldReceive('getRoles')->once()->withNoArgs();

        $rbac->validate($user);
    

    function test_validate_getRolesOnUserReturnsCollectionOfRoles_CallsGetAttributeWithNameOnFirstRole() 
        $rbac = new SomeRBACClass();
        $user = Mockery::mock('UserClass');
         // assuming $user->getRoles() returns a collection
        $collection = new \Illuminate\Support\Collection(array(
             $role1 = Mockery::mock('Role'),
             $role2 = Mockery::mock('Role'),
        ));
        $user->shouldReceive('getRoles')->andReturn($collection);

        $role1->shouldReceive('getAttribute')->once()->with('name');

        $rbac->validate($user);
    

    function test_validate_getAttributeWithNameOnRoleReturnsValidRole_ReturnsTrue() 
        $rbac = new SomeRBACClass();
        $user = Mockery::mock('UserClass');
         // assuming $user->getRoles() returns a collection
        $collection = new \Illuminate\Support\Collection(array(
             $role1 = Mockery::mock('Role'),
             $role2 = Mockery::mock('Role'),
        ));
        $user->shouldReceive('getRoles')->andReturn($collection);
        $role1->shouldReceive('getAttribute')->andReturn('admin');

        $result = $rbac->validate($user);

        $this->assertTrue($result);
    

这不是我要编写的所有单元测试的完整示例,但它是一个开始。例如,我还将验证当没有返回任何角色时,结果是否为假。

【讨论】:

以上是关于如何测试依赖于具有关系的 Eloquent 模型的类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel Eloquent 中构建具有多级关系的查询

Laravel Eloquent 多对多,增量依赖

Laravel/Eloquent - 创建与可能具有或不具有属性的父模型相关的多个子模型的关系

如何在 Eloquent ORM 中从 JSON 中保存具有相关记录的模型

创建具有多种关系的 eloquent 实例?

Eloquent 可以替换数据库中的外键吗?