简体   繁体   中英

Laravel & Mockery - Unit Testing Relational Data

I've got a Posts and a Blog class.

As you can see from below, the Posts class depends on the Blog class.

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

The url for this action is as follows:

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

I'm trying to test this, but I'm running into issues.

Here's my Test Class 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');
}

The question is this... How can I test a get call, when the get itself is contains a variable output based on data? How do I test this correctly?

First of all you have an error in your code. You can drop the all().

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

Second, I'm not aware of a way to unit test route model binding, so I'd change public function index(Blog $blog) to public function index($blogSlug) and then do $this->blog->where('slug', '=', $blogSlug)->first() or something along those lines.

Third, just do m::mock('Post') , drop the Eloquent bit. If you run into problems with this, do m::mock('Post')->makePartial() .

Here's what the test would look like, roughly, if you want to test absolutely everything.

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
}

This serves as a good example that it is difficult to unit test a layer that is coupled to the database layer (in this case, your Blog and Post models are the database layer). I would instead set up a test database, seed it with dummy data and run tests on that, or extract the database logic to a repository class, inject that into the controller and mock that instead of the models.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM