Ad

How To Change Hard Coded Eloquent $connection Only In Phpunit Tests?

- 1 answer

I have an Eloquent Model like this:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;


class SomeModel extends Model
{

    protected $connection = 'global_connection';

......................

The problem is that this $connection has to be hard coded because I have a multi tenant web platform and all the tenants should read from this Database.

But when now in tests I am hitting the Controller route store() and I don't have access to the model! I just do this:

public function store()
{
    SomeModel::create($request->validated());

    return response()->json(['msg' => 'Success']);
}

Which works great when using it as a user through browser...

But now I want to somehow force that model NOT to use that hard coded $connection and set it to Testing database connection...

And this is my Test

/** @test */
public function user_can_create_some_model(): void
{
    $attributes = [
        'name' => 'Some Name',
        'title' => 'Some Title',
    ];

    $response = $this->postJson($this->route, $attributes)->assertSuccessful();
}

Is there any way to achieve this with some Laravel magic maybe :)?

Ad

Answer

Unfortunately for me, none of these answers didn't do the trick because of my specific DB setup for multi tenancy. I had a little help and this is the right solution for this problem:

Create a custom class ConnectionResolver somewhere under tests/ directory in laravel

<?php

namespace Tests;

use Illuminate\Database\ConnectionResolverInterface;
use Illuminate\Database\ConnectionResolver as IlluminateConnectionResolver;

class ConnectionResolver extends IlluminateConnectionResolver
{
    protected $original;
    protected $name;
    public function __construct(ConnectionResolverInterface $original, string $name)
    {
        $this->original = $original;
        $this->name = $name;
    }

    public function connection($name = null)
    {
        return $this->original->connection($this->name);
    }

    public function getDefaultConnection()
    {
        return $this->name;
    }
}

In test use it like this

create a method called create() inside tests/TestCase.php

protected function create($attributes = [], $model = '', $route = '')
{

    $this->withoutExceptionHandling();

    $original = $model::getConnectionResolver();

    $model::setConnectionResolver(new ConnectionResolver($original, 'testing'));

    $response = $this->postJson($route, $attributes)->assertSuccessful();

    $model = new $model;

    $this->assertDatabaseHas('testing_db.'.$model->getTable(), $attributes);

    $model::setConnectionResolver($original);

    return $response;
}

and in actual test you can simply do this:

/** @test */
public function user_can_create_model(): void
{
    $attributes = [
        'name' => 'Test Name',
        'title' => 'Test Title',
        'description' => 'Test Description',
    ];

    $model = Model::class;

    $route = 'model_store_route';        

    $this->create($attributes, $model, $route);
}

Note: that test method can have only one line when using setUp() method and $this-> notation

And that's it. What this does is forcing the custom connection name (which should be written inside config/database.php) and the model during that call will work with that connection no matter what you specify inside the model, therefore it will store the data into DB which you have specified in $model::setConnectionResolver(new ConnectionResolver($original, 'HERE'));

Ad
source: stackoverflow.com
Ad