Summer Sale! All accounts are 50% off this week.

LiamA's avatar
Level 1

local.ERROR: Argument 1 passed to Illuminate\Auth\SessionGuard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, null given

I'm trying to run a simple Dusk test where I create a user, log in as that user, and then assert that the logged in user is an authenticated one. Here is the very simple code:

$user = factory('App\User')->create();
$this->browse(function (Browser $browser) use ($user){
    $browser->loginAs($user)
            ->assertAuthenticated();
});

I'm getting the following error:

local.ERROR: Argument 1 passed to Illuminate\Auth\SessionGuard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, null given

The definition for SessionGuard::login():

/**
 * Log a user into the application.
 *
 * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
 * @param  bool  $remember
 * @return void
 */
public function login(AuthenticatableContract $user, $remember = false)

So I see that as the first parameter it takes a $user object that must implment the \Illuminate\Contracts\Auth\Authenticatable interface.

Now the User model uses the Illuminate\Foundation\Auth\User trait that implements the Illuminate\Contracts\Auth\Authenticatable interface (amongs other things).

So as far as I can see the requirements are met and it should work. Obviously I'm missing something here... Would welcome any suggestions on how to try and fix this. TIA.

0 likes
6 replies
Sergiu17's avatar

Looks like user is not created

$user = factory('App\User')->create();

dd($user); // null
LiamA's avatar
Level 1

Just to clarify, I import the User class in the beginning of the file, and $user IS created

LiamA's avatar
Level 1

I've been trying to dive deeper into the code and investigate what part is causing the error. Here is what I have managed so far:

Dusk's $browser->loginAs($user) is trying to log the user in via this url: /_dusk/login/{$user->id} (in my case /_dusk/login/2). In the browser, /_dusk/login/2 throws the following error:

Argument 1 passed to Illuminate\Auth\SessionGuard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, null given, called in /var/www/laravel/vendor/laravel/dusk/src/Http/Controllers/UserController.php on line 47 

So this is the same error I was getting when running the Dusk test, but now I know that Dusk has it's own User controller with a login function:

/**
     * Login using the given user ID / email.
     *
     * @param  string  $userId
     * @param  string  $guard
     * @return void
     */
    public function login($userId, $guard = null)
    {
        $guard = $guard ?: config('auth.defaults.guard');

        $provider = Auth::guard($guard)->getProvider();
        $user = Str::contains($userId, '@')
                    ? $provider->retrieveByCredentials(['email' => $userId])
                    : $provider->retrieveById($userId);
	
        Auth::guard($guard)->login($user);
    }

the $guard variable equals 'web'.

$provider is an Object of class Illuminate\Auth\EloquentUserProvider

but is statement returns null:

$user = Str::contains($userId, '@') ? $provider->retrieveByCredentials(['email' => $userId]) : $provider->retrieveById($userId);

Actually, both $provider->retrieveByCredentials(['email' => $userId]) and $provider->retrieveById($userId) return null.

Any idea how to proceed from here??

viglucci's avatar

@liama Did you ever figure out what is going on here? I suspect that the issue is that the database that the running application is connected to is not the same one as the one that the unit test populated with the test user... but it has been difficult to track down where the disconnect is.

Edit:

I was ultimately able to get this sorted out by ensuring the following:

  • Remove any ENV variables from phpunit.xml other than APP_ENV
  • APP_ENV should be set to testing in phpunit.xml
  • Use the following database config:
# env.testing
DB_CONNECTION=testing
// config/database.php

'connections' =>  [
        ...
        'testing' => [
            'driver' => 'sqlite',
            'url' => env('DATABASE_URL'),
            'database' => database_path('test.sqlite'),
            'prefix' => '',
            'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
        ],
]

  • Ensure that you run php artisan migrate --env=testing before running tests or starting the test server
  • Ensure you pass the --env=testing flag to php artisan serve when starting the test server (php artisan serve --env=testing).
viglucci's avatar

Additionally to the above steps... you should also NOT leverage the refresh of database migrations traits in your tests. I'll be honest that I'm not 100% sure why, but as soon as I add RefreshDatabase trait to my dusk test that leverages loginAs(...), the tests start failing.

monaye's avatar

@viglucci yes, the document specifically said so including the reasoing. https://laravel.com/docs/8.x/dusk#migrations

Most of the tests you write will interact with pages that retrieve data from your application's database; however, your Dusk tests should never use the RefreshDatabasetrait. The RefreshDatabase trait leverages database transactions which will not be applicable or available across HTTP requests. Instead, use the DatabaseMigrations trait, which re-migrates the database for each test:

Please or to participate in this conversation.