在 Laravel(流明)上使用 Mockery 模拟 Eloquent 模型不起作用

Posted

技术标签:

【中文标题】在 Laravel(流明)上使用 Mockery 模拟 Eloquent 模型不起作用【英文标题】:Mocking Eloquent model with Mockery on Laravel (Lumen) doesn't work 【发布时间】:2020-05-14 04:41:47 【问题描述】:

php 还很陌生,所以如果我遗漏了一些明显的东西,请原谅我。

我目前正在使用 Lumen(它基本上是一个基本的 Laravel,只有核心依赖项)和 Eloquent 来管理持久实体。

我想编写一个单元测试,我需要在其中调用一个函数,该函数在某些时候会执行这样的 Eloquent 查询:

$errorMailRecipients = User::where('id_role',  1)
        ->where('some_value', 1)
        ->whereNotNull('email_addr');

我的 User.class,是一个基本的 Eloquent 实体:

    class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
    
       use Authenticatable, Authorizable, Eloquence, Mappable;

       const CREATED_AT = 'creation_date';
       const UPDATED_AT = 'modification_date';

       /** Eloquent model configuration */
       protected $connection = 'database_name';
       protected $table = 'user';
       protected $primaryKey = 'id';

      // other stuff mapping, functions, etc ...

所以,我创建了一个测试并尝试使用 Mockery 来模拟这个特定的查询:

class ImportTest extends TestCase


    public function test_should_send_error_mail_when_receiving_empty_request()
    
        // Given
        $controller = new ImportController();

        $mockedRecipients = // <- some Collection containing static data
        $mock = Mockery::mock(User::class, function ($mock) use ($mockedRecipients) 
            $mock->shouldReceive('where')->once()->andReturn($mockedRecipients);
        );
        $this->app->instance('App\Models\User', $mock);

        // When
        $controller->importDevices(new Request());

        // Then
    
 

我尝试了许多变体,并将模拟分解为多行:

    $mock = Mockery::mock(User::class);
    $this->app->instance('User', $mock);
    $mock->shouldReceive('where')->once()->andReturn($mockedRecipients);

还尝试删除 ->once(),并阅读以下指南:

https://laravel.com/docs/5.8/mocking http://docs.mockery.io/en/latest/reference/alternative_should_receive_syntax.html

但到目前为止,我在执行 PHP 单元测试时一直出现以下错误:

Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] Connection denied ...

因为,是的,我没有本地数据库,它仍在尝试访问它。有什么想法吗 ? 谢谢

【问题讨论】:

我会称自己为模拟和测试在多个专业项目中看到的 laravel 项目的专家。模拟查询功能是一种浪费,相反我建议使用 Sqllite memory db 吗?如果你想走那条路,我可以告诉你。这些天人们通常只是设置他们的 ci 环境来运行 mysql tbh 并在本地测试时在本地运行它。 它不起作用的原因是它被静态调用 好的,感谢您的见解 :) 我来自 Java 环境,我非常习惯于使用 mockito 库模拟数据库查询。我遇到了像你这样的多个答案,我不太同意。对于集成测试,我完全可以运行内存数据库,放入数据集并从端点测试功能,达到预期结果。但是,就我而言,我想测试与一个函数相关的特定小段代码。 这取决于模拟库的效率,但在我看来,仅模拟最小响应(如 2 个具有少量字段的对象)而不是运行数据库、插入数据、清理数据等... Laravel 具有运行事务以进行清理的功能,或者只使用 Sqlite,只需我的两分钱。我可以让你的代码工作,但会在当天晚些时候做:) 【参考方案1】:

感谢@mrhn,我发现自己做错了;)

Mockery 允许使用别名来模拟公共静态函数,一开始我不知道会有什么不同 :)

只需将Mockery::mock('\App\Models\User'); 替换为Mockery::mock('alias:\App\Models\User'); 就可以了。

因此,不推荐使用它,所以我想我会在测试写作期间继续学习。由于我们很快就会有一个 CI 链直接使用 MySQL 实例化 db 来针对我们的应用程序运行,并且随着 @mrhn 语句的这种方式,我将考虑在未来进行真正的 db 调用。

【讨论】:

以上是关于在 Laravel(流明)上使用 Mockery 模拟 Eloquent 模型不起作用的主要内容,如果未能解决你的问题,请参考以下文章

这个模拟对象上不存在方法 - Laravel,Mockery

在 Laravel 中使用 Mockery/phpUnit 时出错

使用外部 Laravel 护照流明 api 进行 Laravel 客户端身份验证

Laravel Mockery 集成测试

将 laravel 应用程序转换为流明

在 Laravel 4 中使用 Mockery 模拟自定义类