MarceloG's avatar

Laravel feature testing using readonly database connection

Hi! I have a problem with my tests, I'm trying to test an endpoint:

<?php

namespace Tests\Feature\Shift;

use App\Models\Booked;
use App\Models\Employees;
use App\Models\Shift;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;

class OvertimeRuleTest extends TestCase
{
    use DatabaseTransactions;

    private ?Employees $clinician = null;

    private ?User $user = null;

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

        $this->clinician = Employees::factory()->create();
        $this->user = User::factory()->employee($this->clinician)->create();
    }
    /**
     * A basic feature test example.
     */
    public function testExample(): void
    {
        $shift = $this->createShift();
        $response = $this->actingAs($this->user, 'sanctum')
            ->withHeader('x-api-key', config('x-api-key.x-api-key'))
            ->get("shift/$shift->id/actionable");
        $response->assertStatus(200);
    }

    protected function createShift(?array $shiftData = []): Shift
    {
        return Shift::withoutEvents(fn() => Shift::factory(1)
            ->state($shiftData)
            ->has(Booked::withoutEvents(fn() => Booked::factory()->count($shiftData['nurse_count'] ?? 1)))
            ->create())->firstOrFail();
    }
}

But in the controller there is a call to:

        $shift = Shift::on('readonly')->findOrFail($validated['shiftId']);

The problem, I guess, is due to the connection in the controller being set on read-only config. So, the test case transaction data are out of scope when the controller sets up the read-only connection.

I've tried dropping the on() method call and all work very well. But, by requirements, I need to set the read-only connection.

Also, I've tried to set this prop in the test case:

protected $connectionsToTransact = ['mysql', 'readonly'];

But doesn't work. It's looks like Laravel is opening another connection that doesn't know about the data created at mysql connection

Any suggestions or a walkaround? Maybe there is a way to bypass the on method without asking in the code for the environment.

Thanks in advance!

0 likes
1 reply
s4muel's avatar

crazy idea, but worth a try. create a new config entry on config/database.php

'readonly_connection_name' => env('DB_CONNECTION_READONLY', 'readonly')

so you have a place to switch connection. in your controller, instead of "hard-coded" connection name, it will be read from config:

//instead of this:
$shift = Shift::on('readonly')->findOrFail($validated['shiftId']);
//use this:
$shift = Shift::on(config('database.readonly_connection_name'))->findOrFail($validated['shiftId']);

and then you have a way to switch the connection for tests to use the same connection in phpunit.xml

<!-- use your connection name of the database you use for testing instead of mysql_testing -->
<env name="DB_CONNECTION" value="mysql_testing"/>
<env name="DB_CONNECTION_READONLY" value="mysql_testing"/>

good luck

Please or to participate in this conversation.