Laravel & Mockery — модульное тестирование реляционных данных

У меня есть сообщения и класс блога.

Как вы можете видеть ниже, класс Posts зависит от класса Blog.

public function index(Blog $blog) {
    $posts = $this->post->all()->where('blog_id', $blog->id)->orderBy('date')->paginate(20);
    return View::make($this->tmpl('index'), compact('blog', 'posts'));
}

URL-адрес этого действия выглядит следующим образом:

http://example.com/blogs/[blog_name]/posts

Я пытаюсь проверить это, но у меня возникают проблемы.

Вот мой тестовый класс PostTestController:

public function setUp() {
    parent::setUp();
    $this->mock = Mockery::mock('Eloquent', 'Post');
}

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

public function testIndex() {

    $this->mock->shouldReceive('with')->once();

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

    // get posts url
    $this->get('blogs/blog/posts'); //this is where I'm stuck.

    $this->assertViewHas('posts');
}

Вопрос в следующем... Как я могу протестировать вызов get, когда сам get содержит переменный вывод на основе данных? Как правильно это проверить?


person Dan Hanly    schedule 20.05.2014    source источник


Ответы (1)


Прежде всего, у вас есть ошибка в вашем коде. Вы можете отказаться от all().

$posts = $this->post
  ->where('blog_id', $blog->id)
  ->orderBy('date')
  ->paginate(20);

Во-вторых, я не знаю способа привязки модели маршрута модульного тестирования, поэтому я бы изменил public function index(Blog $blog) на public function index($blogSlug), а затем сделал $this->blog->where('slug', '=', $blogSlug)->first() или что-то в этом роде.

В-третьих, просто выполните m::mock('Post'), отбросьте бит Eloquent. Если у вас возникнут проблемы с этим, сделайте m::mock('Post')->makePartial().

Вот как примерно будет выглядеть тест, если вы хотите протестировать абсолютно все.

use Mockery as m;

/** @test */
public function index()
{
    $this->app->instance('Blog', $mockBlog = m::mock('Blog'));
    $this->app->instance('Post', $mockPost = m::mock('Post'));
    $stubBlog = new Blog(); // could also be a mock
    $stubBlog->id = 5;
    $results = $this->app['paginator']->make([/* fake posts here? */], 30, 20);
    $mockBlog->shouldReceive('where')->with('slug', '=', 'test')->once()->andReturn($stubBlog);
    $mockPost->shouldReceive('where')->with('blog_id', '=', 5)->once()->andReturn(m::self())
        ->getMock()->shouldReceive('orderBy')->with('date')->once()->andReturn(m::self())
        ->getMock()->shouldReceive('paginate')->with(20)->once()->andReturn($results);

    $this->call('get', 'blogs/test/posts');
    // assertions
}

Это служит хорошим примером сложности модульного тестирования слоя, связанного со слоем базы данных (в данном случае ваши модели Blog и Post являются слоем базы данных). Вместо этого я бы создал тестовую базу данных, заполнил ее фиктивными данными и провел тесты на ней или извлек логику базы данных в класс репозитория, внедрил ее в контроллер и смоделировал ее вместо моделей.

person Andreas    schedule 22.05.2014