Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

stebrl's avatar

The database session driver is weird, or am I?

Hey, I´m currently working on a feature where users can view all their sessions across multiple devices. To feature test this functionality I´m doing the following:

  • Create the sessions table migration
php artisan session:table
  • Edit phpunit.xml
<server name="SESSION_DRIVER" value="database"/>
  • Edit .env
SESSION_DRIVER=database
  • Edit the frontend
<h1>Sessions</h1>
{{ $sessions }}

@auth
    <form action="logout" method="post">
        @csrf
        <button type="submit">Logout</button>
    </form>
@endauth

@guest
    <form action="login" method="post">
        @csrf
        <button type="submit">Login</button>
    </form>
@endguest
  • Define routes
<?php

use App\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    $sessions = auth()->check()
        ? DB::table('sessions')->whereUserId(auth()->user()->id)->get()
        : collect([]);

    return view('welcome', [
        'sessions' => $sessions,
    ]);
});

Route::post('login', function () {
    auth()->login(User::first());
    return back();
});

Route::post('logout', function () {
    auth()->logout();
    return back();
});

Note: For testing purposes I´ve created a dummy user in the DatabaseTableSeeder and ran php artisan migrate --seed

  • Write the test:
<?php

namespace Tests\Feature;

use App\User;
use Tests\TestCase;
use Illuminate\Support\Facades\DB;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    public function setUp(): void
    {
        parent::setUp();

        $this->user = factory(User::class)->create();
    }

    /** @test */
    function passes()
    {
        $this->actingAs($this->user);

        $this->get('/');

        $this->get('/')
            ->assertViewHas('sessions', DB::table('sessions')->whereUserId($this->user->id)->get());
    }

    /** @test */
    function fails()
    {
        $this->actingAs($this->user)
            ->get('/')
            ->assertViewHas('sessions', DB::table('sessions')->whereUserId($this->user->id)->get());
    }
}

Now my problem is that my tests are not green, but in reality everything works as expected. I´ve created an example repo so you can check out my code: https://github.com/stebrl/database-sessions-test

Would be really nice if someone could help!

0 likes
6 replies
stebrl's avatar

@sti3bas Alright, seems that I didn´t dive deep enough to find that lol, thanks for that!

But why is it working when I test it in the browser? What´s the difference to my test, or how do I need to rewrite my code to get confidence with a test? I´m clueless haha

Sti3bas's avatar

@stebrl it works the same way, it looks different because you're making a redirect after logging in the user (so your session is saved after /login request is executed). If you would update your route to this, you would not see any sessions in the page, because it would only be saved after preparing the view:

Route::get('/', function () {
    auth()->login(User::first());
    $sessions = auth()->check()
        ? DB::table('sessions')->whereUserId(auth()->user()->id)->get()
        : collect([]);

    return view('welcome', [
        'sessions' => $sessions,
    ]);
});
1 like
Sti3bas's avatar
Sti3bas
Best Answer
Level 53

@stebrl I would probably create a factory, something like:

factory(Session::class, 3)->create();

$userSessions = factory(Session::class, 2)->create([
   'user_id' => $this->user->id,
]);

factory(Session::class, 1)->create();

$this->actingAs($this->user)
   ->get('/sessions')
   ->assertViewHas('sessions', function($sessions) use ($userSessions)  {
       return $sessions->count() === 2 && $sessions->contains($userSessions->pluck('id'));
   });

https://laravel.com/docs/7.x/database-testing#writing-factories

1 like
Sti3bas's avatar

@stebrl if you want to have a test which proves that StartSession middleware is added to web group, you can use something like this:

// add dummy route
Route::get('/test', function () {});

$this->assertEquals(0, DB::table('sessions')->count());

$this->actingAs($this->user)->get('/');

tap(DB::table('sessions')->get(), function ($sessions) {
    $this->assertCount(1, $sessions);
    $this->assertEquals($this->user->id, $sessions[0]->user_id);
});

Please or to participate in this conversation.