2016-04-19 4 views
2

Я использую laravel 5 с модулем php для создания пакета laravel. У меня есть Repository ..Mockery and Laravel constructor injection

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); 
    } 
} 

..which реализует 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) Myname \ Myapptests \ блок \ PersonRepositoryTest :: it_returns_the_test_find ErrorException: Аргумент 1 передается Myname \ MyApp \ Хранилища \ PersonRepository :: __construct() должен быть экземпляром Myname \ Myapp \ Models \ PersonModel, например, насмешки \ CompositeExpectation данного

из того, что я прочитал, издевательство расширяет класс он издевается, так что не должно быть никакой проблемы вводя расширенный класс вместо типа намеченного родителя (PersonModel)

Очевидно, что я чего-то не хватает. В других примерах явным образом вводят издеваемый объект в класс, который они тестируют. Laravels IoC (должно быть) делает это для меня. Нужно ли мне что-то связывать?

У меня есть ощущение, что объект издевательств создается не так, как я думаю (расширение PersonModel), иначе я предполагаю, что не увижу эту ошибку.

+0

Не могли бы вы попытаться импортировать свою модель? вы используете 'PersonModel :: class', но я не вижу' use 'Myname \ Myapp \ Models \ PersonModel'' –

+0

@FabioAntunes Обновлен код и вывод ошибки – myol

ответ

6

Проблема заключается в том, когда вы создаете макет:

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

Блестящее подробное объяснение, спасибо. – myol

+0

@myol рад, что я помог вам –

+2

, просто подумал, что добавлю, что вы можете делать '-> с ('var') -> getMock()', тогда можете объявить свои ложные и жидкие цепочки, чтобы получить фактический макет объекта – wired00

2

В то время как Фабио дает great answer, вопрос здесь действительно испытательная установка. 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-дружественных и поможет вам, когда код рефакторинга позже.