Laravel 5: Handle multiple connections and testing

旧巷老猫 提交于 2020-04-06 04:45:27

问题


I have a Laravel 5.4 app which has models pointing to different database connections.

For example, I have User pointing to a MySQL database and then Company pointing to a PostgreSQL database (using the $connection variable).

Now, when I run PHPUnit I'd like the $connection variable to be replaced by what's specified in the phpunit.xml file, which is a SQLite in memory type of database.

How is that achievable?


回答1:


Most answers are changing production code, which I don't like.

Since \Illuminate\Contracts\Foundation\Application is available in your tests, let's use it!

<?php

declare(strict_types=1);

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Company;    

class CompanyFeatureTest extends TestCase
{
    /**
     * @return void
     */
    protected function setUp(): void
    {
        parent::setUp();

        $this->app->bind(Company::class, function () {
            return (new Company())->setConnection(config('database.default'));
        });
    }
}

Whenever your Company class is called, we give back a manipulated one.
In this one we have changed the $connection property.

If you have the following in your phpunit.xml:

<server name="DB_CONNECTION" value="sqlite"/>

The value of config('database.default') will be sqlite.

More info about binding can be found here: https://laravel.com/docs/5.8/container#binding




回答2:


I'd rather not touch the production code and instead use the service container to create test-specific services.

In this case, if you want all your models to use the same default testing connection:

public function createApplication()
{
    $app = require __DIR__.'/../bootstrap/app.php';

    $app->make(Kernel::class)->bootstrap();

    $fakeManager = new class ($app, $app['db.factory']) extends DatabaseManager {
        public function connection($name = null) {
            return parent::connection($this->getDefaultConnection());
        }
    };
    $app->instance('db', $fakeManager);
    Model::setConnectionResolver($fakeManager);

    return $app;
}

(This overrides the CreatesApplication trait, you could instead place this code anywhere between when the app is bootstrapped and when the migration command is called).

(Also note this is using PHP 7 inline anonymous classes. You could also define a fake db manager as a separate class).




回答3:


Off the top of my head you could move the connection names to the .env file

in your model:

public function __construct(array $attributes = [])
{
    $this->connection = env('MY_CONNECTION');
    parent::__construct($attributes);
}

in your .env file

MY_CONNECTION=mysql

in phpunit.xml

<env name="MY_CONNECTION" value="sqlite"/>



回答4:


As mentioned before, you first need to set the connection in each Model. So, you setup the connections in the database config file, set the values in the .env file and use these in the Model's constructors.

For testing, you could also do this. Add the testing connection to the config/database.php file and then use an overriding env file.

Create an additional env file, name it something like .env.testing.

So, in your .env file you will have:

CONNECTION_MYSQL=mysql
CONNECTION_POSTGRESS=postgress

Then in the .env.testing file you can have:

CONNECTION_MYSQL=test_sqlite
CONNECTION_POSTGRESS=test_sqlite

Finally to load this env file when testing, go to CreatesApplication trait and update to the following:

public function createApplication()
{
    $app = require __DIR__.'/../bootstrap/app.php';

    $app->loadEnvironmentFrom('.env.testing');

    $app->make(Kernel::class)->bootstrap();

    return $app;
}

By using the loadEnvironemtFrom() method, all tests that use this trait will load the .env.testing file and use the connections defined there.




回答5:


Like Arturo Rojas wrote in his answer you have to check in the constructor, if the connection variable has to be overwritten:

public function __construct(array $attributes = [])
{
    if(App::environment() == 'testing') {
        $this->connection = env('DB_CONNECTION');
    }
    parent::__construct($attributes);
}

In your phpunit.xml you need these variables (DB_DATABASE is optional):

<php>
    <env name="APP_ENV" value="testing"/>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value="testDatabase"/>
<php>

Laravel will then use the sqllite connection from /config/database.php



来源:https://stackoverflow.com/questions/43474356/laravel-5-handle-multiple-connections-and-testing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!