How to logout within one test in Laravel testing?

Viewed 4925

Laravel v5.7.1

I have multiple tests with same assertions and want to move them in one function and call it from tests. Here is example of function:

private function admin_only($url, $method = 'GET', $data = []) {
    // \Auth::logout();
    $response = $this->json($method, $url, $data);
    $response->assertStatus(401);
    $response = $this->actingAs($this->user(),'api')->json($method, $url, $data);
    $response->assertStatus(403);

    $response = $this->actingAs($this->admin(),'api')->json($method, $url, $data);
    $response->assertStatus(200);
}

In this I firstly check for unauthenticated user and everything works as expected, but there are some functions, where API calls toggle some state, so I want to revert it back by calling function second time:

$this->admin_only('/api/service/toggle-state', 'POST', $data);
$this->admin_only('/api/service/toggle-state', 'POST', $data);

And second call in same test results in failure because first $this->json() used as admin and returns success code.

That \Auth::logout() supposed to solve problem, but instead throws error BadMethodCallException: Method Illuminate\Auth\RequestGuard::logout does not exist.

Solutions like "Separate test for second call", "No toggle revert", "Same response code for non-admin users and guests" should work, but seems wrong for me.

2 Answers

Luckily RequestGuard is Macroable, so we can extend it with our own methods. Try to do the following:

use Illuminate\Auth\RequestGuard;

// ...

RequestGuard::macro('logout', function() {
    $this->user = null;
});

$this->app['auth']->guard('api')->logout(); // or omit `->guard('api')` part

This is opposite of what ->actingAs does. All the following requests will not have the authenticated user.

This can be added to the base test class as a method, and then used as $this->actingAsGuest() or something similar. Anyway, it would be convenient to have this by default in Laravel.

it might you have different guards >> so this might work

Auth::guard('admin')->logout();

you can change 'admin' to whatever the guard name

Related