我正在使用 laravel 5 和 php 单元来创建 laravel 包。我有一个
Repository
..
namespace Myname\Myapp\Repositories;
use Myname\Myapp\Models\PersonModel;
class PersonRepository
{
protected $personModel;
public function __construct(PersonModel $personModel)
{
$this->personModel = $personModel;
}
public function testFunction($var)
{
return $this->personModel->find($var);
}
}
..它实现了
Model
.
namespace Myname\Myapp\Models;
use Illuminate\Database\Eloquent\Model;
class PersonModel extends Model
{
protected $table = 'person';
}
Laravels IoC 自动将
PersonModel
注入到 PersonRepository
的构造函数中。
我正在编写一个单元测试,我想使用模拟来模拟
PersonModel
模型,这样我在测试期间就不会访问数据库。
namespace Myname\Myapptests\unit;
use Mockery;
class PersonRepositoryTest extends \Myname\Myapptests\TestCase
{
/**
* @test
*/
public function it_returns_the_test_find()
{
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
->shouldReceive('find')
->with('var');
$this->app->instance('Myname\Myapp\Models\PersonModel', $mock);
$repo = $this->app->make('Myname\Myapp\Repositories\PersonRepository');
$result = $repo->testFunction('var');
$this->assert...
}
}
当我运行测试时,出现错误
1) 我的名字\Myapptests\unit\PersonRepositoryTest::it_returns_the_test_find ErrorException:传递给 Myname\Myapp\Repositories\PersonRepository::__construct() 的参数 1 必须是 Myname\Myapp\Models\PersonModel 的实例,给定的 Mockery\CompositeExpectation 的实例
根据我所读到的内容,mockery 扩展了它正在模拟的类,因此注入扩展类来代替类型暗示的父类(PersonModel)应该没有问题
显然我错过了一些东西。其他示例将模拟对象显式注入到他们随后测试的类中。 Laravels IoC 正在(应该)为我做这件事。我需要绑定什么东西吗?
我有一种感觉,嘲笑对象没有按照我认为的方式创建(扩展 PersonModel),否则我认为我不会看到这个错误。
问题是当你创建模拟时:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
->shouldReceive('find')
->with('var');
所以这个:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
var_dump($mock);
die();
会输出类似这样的内容:
Mockery_0_Myname_Myapp_Models_PersonModel
但是这个:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
->shouldReceive('find')
->with('var');
var_dump($mock);
die();
将输出:
Mockery\CompositeExpectation
所以尝试做这样的事情:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel');
$mock->shouldReceive('find')->with('var');
$this->app->instance('Myname\Myapp\Models\PersonModel', $mock);
$repo = $this->app->make('Myname\Myapp\Repositories\PersonRepository');
$result = $repo->testFunction('var');
虽然 Fabio 给出了很好的答案,但这里的问题实际上是测试设置。 Mockery 的模拟对象确实遵守契约,并将通过
instanceof
测试和方法参数中的类型提示。
原始代码的问题在于,被调用的方法链最终返回的是期望而不是模拟。我们应该首先创建一个模拟,然后向该模拟添加期望。
要修复它,请更改此:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
->shouldReceive('find')
->with('var');
进入此:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel');
$mock->shouldReceive('find')->with('var');
变量
$mock
现在将实现PersonModel
。
奖金:
不要使用
'Myname\Myapp\Models\PersonModel'
,而使用 PersonModel::class
。这对 IDE 更加友好,并且会在稍后重构代码时为您提供帮助。
我认为在创建模拟时流畅地链接
shouldReceive()
方法是可以的。如果将 getMock()
函数链接到创建模拟的语句末尾,您将返回实例。
所以:
$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
->shouldReceive('find')
->with('var')
->getMock();