Dwight
Dwight

Reputation: 12440

Mocking in controller tests with Laravel model binding

I'm using model binding within my routes to pass models into my controller actions and would like to be able to write tests. It would be preferable if it wasn't required for the test to hit the database.

The model is bound using the username in this example, and then used in the definition of the routes.

// routes.php
Route::model('user', function($value, $route)
{
    return User::whereUsername($value)->firstOrFail();
});

Route::get('users/{user}', 'UsersController@show');

In my controller the bound user is passed to the action.

// UsersController.php
function show(User $user)
{
    return View::make('users.show', compact('user');
}

Now, in my tests I'm attempting to mock the User.

// UsersControllerTest.php
public function setUp()
{
    parent::setUp();

    $this->mock = Mockery::mock('Eloquent', 'User');
    $this->app->instance('User', $this->mock);
}

public function testShowPage()
{
    $this->mock->shouldReceive('whereSlug')->once()->andReturn($this->mock);

    $this->action('GET', 'UsersController@show');

    $this->assertResponseOk();
    $this->assertViewHas('user');
}

When running this test, I get the following error:

ErrorException: Argument 1 passed to UsersController::show() must be an instance of User, instance of Illuminate\Database\Eloquent\Builder given

I'd also like to be able to use return User::firstByAttribtues($value); but Mockery won't let me mock a protected method - is there any way I can get around this?

Upvotes: 0

Views: 4108

Answers (2)

Alex Goodwin
Alex Goodwin

Reputation: 11

I had to dig thru Mockery's source code to find this, but have you looked at shouldAllowMockingProtectedMethods ?

Ie, to mock class foo and allow protected methods to be mocked:

$bar = \Mockery::mock('foo')->shouldAllowMockingProtectedMethods();
// now set your expectations up

and then keep going from there.

Upvotes: 1

awei
awei

Reputation: 1164

Not sure why you're not getting an error like unexpected method "firstOrFail" called. But, at first glance, I think the problem is that your model route defined in routes.php is also calling the firstOrFail method.

So, your test should look something like this:

public function testShowPage()
{
    $stubQuery = \Mockery::mock('Illuminate\Database\Eloquent\Builder');
    $this->mock->shouldReceive('whereSlug')->once()->andReturn($stubQuery);
    $stubQuery->shouldReceive('firstOrFail')->andReturn($this->mock);


    $this->action('GET', 'UsersController@show');

    $this->assertResponseOk();
    $this->assertViewHas('user');
}

Upvotes: 0

Related Questions