Laravel 5:处理多个连接和测试

时间:2017-04-18 14:00:02

标签: laravel laravel-5 eloquent phpunit

我有一个Laravel 5.4应用程序,它的模型指向不同的数据库连接。

例如,我有User指向MySQL数据库,然后Company指向PostgreSQL数据库(使用$connection变量)。

现在,当我运行PHPUnit时,我希望将$connection变量替换为phpunit.xml文件中指定的内容,该文件是内存类型数据库中的SQLite。

这怎么可以实现?

5 个答案:

答案 0 :(得分:3)

我可以将连接名称移动到.env文件

在您的模型中:

public function __construct(array $attributes = [])
{
    $this->connection = env('MY_CONNECTION');
    parent::__construct($attributes);
}
你的.env文件中的

MY_CONNECTION=mysql

在phpunit.xml中

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

答案 1 :(得分:2)

我宁愿不接触生产代码,而是使用服务容器来创建特定于测试的服务。

在这种情况下,如果您希望所有模型使用相同的默认测试连接:

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;
}

(这会覆盖CreatesApplication特征,您可以将此代码放在介绍应用程序和调用迁移命令之间的任何位置。

(另请注意,这是使用PHP 7内联匿名类。您还可以将伪数据库管理器定义为单独的类。)

答案 2 :(得分:2)

大多数答案正在更改生产代码,我不喜欢。

由于\Illuminate\Contracts\Foundation\Application在您的测试中可用,因此请使用它!

<?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'));
        });
    }
}

无论何时调用您的Company类,我们都会归还一个经过操纵的类。
在这一步中,我们更改了$connection属性。

如果phpunit.xml中包含以下内容:

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

config('database.default')的值为sqlite

有关绑定的更多信息,请参见:https://laravel.com/docs/5.8/container#binding

答案 3 :(得分:1)

如前所述,您首先需要在每个模型中设置连接。 因此,您在数据库配置文件中设置连接,在import ConfigParser import os class ConfigManager(object): """docstring for ConfigManager.""" def __init__(self): super(ConfigManager, self).__init__() self.path = os.path.join( os.environ['HOME'], 'bin/config' ) def get_config(self): parser = ConfigParser.ConfigParser() print(os.path.join(self.path, 'Profile.ini')) parser.read( os.path.join(self.path, 'Profile.ini') ) for section_name in parser.sections(): print('Section:', section_name) print(' Options:', parser.options(section_name)) for name, value in parser.items(section_name): print(' %s = %s' % (name, value)) print() config_path = os.path.join( self.path, parser.get('Profiles', 'ActiveProfile'), 'Configuration.ini' ) config_profile = parser.read(config_path) print(config_profile) 文件中设置值并在模型的构造函数中使用它们。

进行测试时,您也可以这样做。 将测试连接添加到.env文件,然后使用重写的env文件。

创建一个额外的env文件,将其命名为config/database.php

因此,在您的.env.testing文件中,您将拥有:

.env

然后在CONNECTION_MYSQL=mysql CONNECTION_POSTGRESS=postgress 文件中,您可以:

.env.testing

最后要在测试时加载此env文件,请转到CONNECTION_MYSQL=test_sqlite CONNECTION_POSTGRESS=test_sqlite 特征并更新到以下内容:

CreatesApplication

通过使用public function createApplication() { $app = require __DIR__.'/../bootstrap/app.php'; $app->loadEnvironmentFrom('.env.testing'); $app->make(Kernel::class)->bootstrap(); return $app; } 方法,所有使用此特征的测试都将加载loadEnvironemtFrom()文件并使用其中定义的连接。

答案 4 :(得分:0)

就像Arturo Rojas在他的回答中写道,如果连接变量必须被覆盖,你必须检查构造函数:

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

phpunit.xml中,您需要这些变量(DB_DATABASE是可选的):

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

然后,Laravel将使用/config/database.php

中的sqllite连接