嘲弄和拉拉维尔构造函数注入

2022-08-30 22:57:14

我正在使用带有php单元的laravel 5来创建一个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: 参数 1 传递给 Myname\Myapp\Repositories\PersonRepository::__construct() 必须是 Myname\Myapp\Models\PersonModel 的实例,给定 Mockery\CompositeExpectation 的实例

从我所读到的内容来看,嘲笑扩展了它所嘲笑的类,所以注入扩展的类来代替类型提示的父级(PersonModel)应该没有问题。

显然我错过了一些东西。其他示例将模拟对象显式注入到它们随后测试的类中。Laravels IoC(应该)为我做这件事。我必须绑定任何东西吗?

我有一种感觉,虽然嘲笑对象不是以我认为的方式创建的(扩展PersonModel),否则我认为我不会看到这个错误。


答案 1

问题是当你创建模拟时:

$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');

答案 2

虽然Fabio给出了一个很好的答案,但这里的问题实际上是测试设置。Mockery 的 mock 对象确实符合协定,并将在方法参数中通过测试和类型提示。instanceof

原始代码的问题在于,被调用的方法链最终返回期望而不是模拟。相反,我们应该先创建一个模拟,然后为该模拟添加期望。

要修复此问题,请更改以下内容:

$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
    ->shouldReceive('find')
    ->with('var');

进入这个:

$mock = Mockery::mock('Myname\Myapp\Models\PersonModel');
$mock->shouldReceive('find')->with('var');

该变量现在将实现 。$mockPersonModel

奖金:

请使用 而不是 ,请使用 。这对 IDE 更加友好,在以后重构代码时会对您有所帮助。'Myname\Myapp\Models\PersonModel'PersonModel::class


推荐