在 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 时出错