mardi 3 novembre 2015

How to mock User facade in Laravel 5.x on controller using Auth middleware?

Using a TDD approach, I'm working on an "edit" controller action. The first step is to get the "activity" using an Eloquent relationship (activities) on the User model:

public function edit($activity)
{
    $userActivity = Auth::user()->activities()->find($activity);
    return 'edit';
}

Since the controller is using the Auth middleware, I need to "be" a user in order to get to the route, so my test looks like this so far:

public function test_UserCannotEditAnActivityThatDoesNotBelongToThem()
{
    // Arrange
    $activityId = 10;
    $this->be(Fakes::authorizedUser());
    Auth::shouldReceive('user->activities->find')
        ->once()
        ->andReturn(null);

    // Act
    $this->call('GET', '/activity/' . $activityId . '/edit');

    // Assert

}

(Assertion is intentionally missing as it obscures the error message.) As you can see, I'm using a fake user, but this has been working well for me. However, this fails to pass with this message:

Method find() from Mockery_7__demeter_activities should be called exactly 1 times but called 0 times.

I've tried to read everything I can about mocking facades, but haven't stumbled onto the answer yet. The Laravel documentation is pretty straight forward and mostly refers to the Mockery documentation, which I found wonderfully useful for mocking fluent interfaces/chains.

The clue that I've found is that if I exclude the edit action from the auth middleware, it works. Except that would be bad, so I don't want to do that.

The other clue is that stepping through the code shows that the edit method doesn't even get hit (which would explain why the mock fails). Removing the Auth::shouldReceive() expectation allows the route to be found.

So it seems like there's some bad juju between the the middleware, my fake user, and the mocked Auth... but I just can't figure out what.

And if you're wondering what the Fakes::authorizedUser looks like:

static function authorizedUser(Array $overrides = [])
{
    Model::unguard();
    $goodData = [
        'id'         => 1,
        'first_name' => 'Chuck',
        'last_name'  => 'Norris',
        'email'      => 'pain@example.com',
        'password'   => 'roundhouse'
    ];

    return new User(array_merge($goodData, $overrides));
}

The code actually "works" fine. When I access it from the browser and step through it, it behaves as expected. So I've definitely botched something in my testing.

Thanks.



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire