Попытка подкачки модели в моем тесте контроллера с помощью контейнера IoC, но объект не подкачивает

Я пытаюсь использовать контейнер IoC для замены моей модели вопросов при тестировании. Хотя я создал фиктивную модель и использовал App::instance(), чтобы попытаться поменять местами зависимость во время моего теста, я вижу из var_dump, что она не работает. Что не так с моим кодом?

<?php

class QuestionsControllerTest extends TestCase {

    protected $mock;

    public function __construct()
    {
        // This is how Net tuts tutorial instructed, but 
        // I got Eloquent not found errors
        // $this->mock = Mockery::mock('Eloquent', 'Question');

        // so I tried this instead, and it created the mock
        $this->mock = Mockery::mock('App\Question'); 
    }

    public function tearDown()
    {
        Mockery::close();
    }

    public function testQuestionIndex()
    {
        // var_dump(get_class($this->mock)); exit; // outputs: Mockery_0_App_Question
        // var_dump(get_class($this->app)); exit; // outputs: Illuminate\Foundation\Application

       $this->mock
           ->shouldReceive('latest')
           ->once()
           ->andReturnSelf();

        $this->mock
            ->shouldReceive('get') //EDIT: should be get
            ->once()
            ->andReturn('foo');

    $this->app->instance('App\Question', $this->mock);


        // dispatch route

        $response = $this->call('GET', 'questions');

        $this->assertEquals(200, $response->getStatusCode());
    }
}

Все идет нормально? Ниже мой QuestionsController:

class QuestionsController extends Controller {

    protected $question;

    public function index(Question $question)
    {
        // var_dump(get_class($question)); exit; // Outputs App\Question when testing too

        $questions = $question
            ->latest()
            ->get();

        return view('questions.index', compact('questions'));
    }
    ...

Таким образом, без замены объекта он все равно не регистрирует вызов методов:

Mockery\Exception\InvalidCountException: Method latest() from Mockery_0_App_Question should be called
 exactly 1 times but called 0 times.

Кстати, я установил Mockery ~0.9, Laravel 5.0 и PHPUnit ~4.0. Был бы очень признателен за любую помощь в этом.


person Martyn    schedule 16.02.2015    source источник
comment
Я не знаком с модульным тестированием laravel или php, поэтому этот комментарий может быть неуместным, но я не подключаю зависимости, используя контейнер IoC для модульных тестов. Вместо этого я бы создал экземпляр QuestionsController напрямую и передал зависимость.   -  person Matthew    schedule 27.02.2015
comment
Класс тестового примера Laravel, похоже, имеет множество встроенных утверждений для тестирования таких вещей, как переменные, которые передаются в представление, возвращаемый код состояния и т. д. Я не могу представить, чтобы они были доступны при тестировании контроллера независимо от приложения? Я предполагаю, что в строгом смысле модульного тестирования это может сработать, я не использовал Laravel достаточно долго. Я попробую это как-нибудь, хотя, если ничего не получится, спасибо.   -  person Martyn    schedule 28.02.2015


Ответы (2)


Я думаю, вам нужно указать полное пространство имен при использовании instance(). В противном случае Laravel будет считать, что модель находится в глобальном пространстве имен ('\Question').

Это должно работать:

$this->app->instance('App\Question', $this->mock);

Теперь о другой проблеме, вашей насмешке. Поскольку ваш взгляд хочет коллекцию, почему бы просто не дать ей ее? И если вы не хотите тестировать представление, вы можете просто создать пустую коллекцию и вернуть ее:

$this->mock
     ->shouldReceive('latest')
     ->once()
     ->andReturnSelf();

$this->mock
     ->shouldReceive('get')
     ->once()
     ->andReturn(new Illuminate\Database\Eloquent\Collection);

Если вы хотите, вы также можете проверить, правильно ли представление получило переменную:

$response = $this->call('GET', 'questions');

$this->assertViewHas('questions');
person lukasgeiter    schedule 27.02.2015
comment
Привет, спасибо, теперь это, похоже, передается в объект, но теперь я получаю ответ 500, поэтому мое утверждение статуса не выполняется. Это не дает мне никакого сообщения об ошибке, кроме Failed asserting that 500 matches expected 200. - person Martyn; 27.02.2015
comment
Вероятно, потому что вы указываете только, что latest() должен вызываться и что он должен возвращать, поэтому вызов get() для результата вызывает исключение. (см. другой ответ на этот вопрос) Если вы хотите получить более подробную информацию об ошибке, посмотрите файл журнала в storage/logs - person lukasgeiter; 27.02.2015
comment
Да. Кажется, и недопустимая переменная для foreach в представлении, поскольку фиктивный элемент возвращает не экземпляр Eloquent\Collection, а вместо этого строку foo :) Могу ли я издеваться над Eloquent Collection? Или отключить рендеринг вида из теста? Как это должно быть обработано в тестах контроллера? Если я верну foo, это сломает мое представление (например, foreach, свойства и т. д.).. в любом случае, медленно добираюсь, спасибо - person Martyn; 28.02.2015
comment
Благодарю вас! Извините, на самом деле я должен был сделать это сам. Я думал, что мне нужно издеваться над всем, но пустая коллекция абсолютно подойдет. Во всяком случае, все работает с моим тестом. Баунти твоя. (ОБНОВЛЕНИЕ: в нем говорится, что по какой-то причине мне нужно подождать 3 часа, тогда я присужу его :) - person Martyn; 28.02.2015

Вы получаете эту ошибку, поскольку не определили свой макет полностью.

вы сказали своему макету, что он должен ожидать, что latest будет вызван один раз, но вы не указали, что должен вернуть последний. в следующей строке вашего контроллера вы вызываете get.

Попробуйте следующий код

    $this->mock
       ->shouldReceive('latest')
       ->once()
       ->andReturnSelf();


     $this->mock
       ->shouldReceive('get') //EDIT: should be get
       ->once()
       ->andReturn('foo');

    $this->app->instance('Question', $this->mock);

Очень хорошая статья о тестировании контроллеров в larvel http://code.tutsplus.com/tutorials/testing-laravel-controllers--net-31456

person DouglasDC3    schedule 27.02.2015
comment
Привет, спасибо за пост. Да, я следил за этой статьей, но это не сработало для меня. Как уже упоминалось, когда я вывожу get_class($question) в контроллере при запуске теста, я вижу, что это не фиктивный объект. Тем не менее, предыдущий автор предложил использовать полный путь $this->app->instance('App\Question', $this->mock);, и это действительно передает объект, но теперь я получаю ошибку 500, поэтому мое утверждение кода состояния не выполняется. - person Martyn; 27.02.2015