'Laravel actingAs guest
Laravel provides a way to authenticate a given user during HTTP testing with
$this->actingAs($user);
Is there a way to unauthenticate that $user
within the same test?
Solution 1:[1]
Yes, you can unauthenticate using this:
Auth::logout();
https://laravel.com/docs/7.x/authentication#logging-out
Warning: above does far more than just forgetting (acting as if the login did not happen), for example, when using JWT above should invalidate token.
Solution 2:[2]
Yes, define new actingAsGuest
method in base TestCase
class
in file tests/TestCase.php
<?php
namespace Tests;
use Illuminate\Auth\RequestGuard;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp(): void
{
parent::setUp();
// add logout method to RequestGuard
RequestGuard::macro('logout', function() {
$this->user = null;
});
}
// add method to base TestCase class
public function actingAsGuest(): void
{
$this->app['auth']->logout();
}
}
And then in your test class you can use it:
<?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function test_example()
{
// acting as authenticated user
$this->actingAs(User::factory()->create());
$this->assertAuthenticated();
// acting as unauthenticated user
$this->actingAsGuest();
$this->assertGuest();
}
}
Solution 3:[3]
I had same requirements as OP did, but wanted actingAsGuest()
to completely reset everything, except Database state.
Full App reset (except DB)
For Laravel 7 (and maybe newer or older)
I toke a look at Laravel's tearDown()
and setUp()
methods.
And came up with helper method like:
use Illuminate\Support\Facades\Facade;
// ...
function actingAsGuest()
{
// Backup database state.
/** @var \Illuminate\Database\MySqlConnection $connection */
$connection = app('db.connection');
// Reset everything else.
/** @var \Illuminate\Foundation\Application $app */
$app = $this->app;
$app->flush();
$this->app = null;
Facade::clearResolvedInstances();
$this->refreshApplication();
// Restore database state.
app('db')->extend($connection->getName(), function () use ($connection) {
return $connection;
});
}
WARNING !!
Above works fine unless your test's logic caches any of above discarded objects somewhere.
For example, Laravel's DatabaseTransactions
trait did cache db
facade (in their App-Destroyed-listener).
Which we fixed by overriding said trait's logic.
Like we changed:
// ... $this->beforeApplicationDestroyed(function () use ($database) { // ...
Into:
// ... $this->beforeApplicationDestroyed(function () { $database = app('db'); // ...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Top-Master |
Solution 2 | Lukas Pierce |
Solution 3 |