Mockery和Laravel构造函数注入

myo*_*yol 5 php phpunit laravel mockery

我正在使用带有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);
    }
}
Run Code Online (Sandbox Code Playgroud)

..实现了Model.

namespace Myname\Myapp\Models;

use Illuminate\Database\Eloquent\Model;

class PersonModel extends Model
{
    protected $table = 'person';
}
Run Code Online (Sandbox Code Playgroud)

Laravels IoC自动注入PersonModel到构造函数中PersonRepository.

我正在编写单元测试,我想使用mock来模拟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...
     }
}
Run Code Online (Sandbox Code Playgroud)

当我运行测试时,我收到一个错误

1)Myname\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),否则我认为我不会看到这个错误.

Fab*_*nes 10

问题是当你创建你的模拟时:

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

所以这:

$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
var_dump($mock);
die();
Run Code Online (Sandbox Code Playgroud)

会输出这样的东西: Mockery_0_Myname_Myapp_Models_PersonModel

但是这个:

$mock = Mockery::mock('Myname\Myapp\Models\PersonModel')
    ->shouldReceive('find')
    ->with('var');
var_dump($mock);
die();
Run Code Online (Sandbox Code Playgroud)

输出这个: 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');
Run Code Online (Sandbox Code Playgroud)

  • 只是以为我会添加你可以做` - > with('var') - > getMock()`然后可以声明你的模拟和流体链调用来获取实际的模拟对象 (2认同)

Tor*_*sen 8

虽然Fabio给出了一个很好的答案,但这里的问题实际上是测试设置.Mockery的模拟对象确实符合契约,并将instanceof在方法参数中传递测试和类型提示.

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

要修复它,请更改:

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

进入:

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

该变量$mock现在将实现PersonModel.

奖金:

而不是'Myname\Myapp\Models\PersonModel',使用PersonModel::class.这是一个更友好的IDE,并将在以后重构代码时帮助您.