嘲弄和 Laravel 构造函数注入

Mockery and Laravel constructor injection

我正在使用 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 自动注入 PersonModelPersonRepository 的构造函数中。

我正在编写一个单元测试,我想在其中使用模拟来模拟 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) Myname\Myapptests\unit\PersonRepositoryTest::it_returns_the_test_find ErrorException: Argument 1 passed to Myname\Myapp\Repositories\PersonRepository::__construct() must be an instance of Myname\Myapp\Models\PersonModel, instance of Mockery\CompositeExpectation given

根据我的阅读,嘲笑扩展了 class 它是嘲弄的,所以注入扩展的 class 代替类型提示的父级 (PersonModel)[=20= 应该没有问题]

显然我遗漏了一些东西。其他示例显式地将模拟对象注入到他们正在测试的 class 中。 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 给出了 , the issue here is really the test setup. Mockery's mock objects do comply to contracts 并将通过 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-friendly,将在以后重构代码时对您有所帮助。