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

Posted

技术标签:

【中文标题】Laravel 5:处理多个连接和测试【英文标题】:Laravel 5: Handle multiple connections and testing 【发布时间】:2017-09-14 10:07:03 【问题描述】:

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

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

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

如何实现?

【问题讨论】:

为什么给定的答案不适合您的问题? 【参考方案1】:

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

由于\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

【讨论】:

【参考方案2】:

如前所述,首先需要在每个模型中设置连接。 因此,您在数据库配置文件中设置连接,在 .env 文件中设置值并在模型的构造函数中使用这些值。

对于测试,您也可以这样做。 将测试连接添加到 config/database.php 文件,然后使用覆盖的 env 文件。

创建一个额外的 env 文件,将其命名为 .env.testing

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

CONNECTION_MYSQL=mysql
CONNECTION_POSTGRESS=postgress

然后在.env.testing 文件中你可以拥有:

CONNECTION_MYSQL=test_sqlite
CONNECTION_POSTGRESS=test_sqlite

最后在测试的时候加载这个env文件,去CreatesApplication trait并更新如下:

public function createApplication()

    $app = require __DIR__.'/../bootstrap/app.php';

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

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

    return $app;

通过使用loadEnvironemtFrom() 方法,所有使用此特征的测试都将加载.env.testing 文件并使用其中定义的连接。

【讨论】:

【参考方案3】:

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

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

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 内联匿名类。您还可以将假数据库管理器定义为单独的类)。

【讨论】:

这是一个很好的解决方案。您可以将此代码放在tests/TestCase.php createApplication() 中。我还编辑了假管理器以将连接名称转换为其他名称,因为我想保持我的测试连接分开。【参考方案4】:

就像 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 连接

【讨论】:

【参考方案5】:

您可以将连接名称移到 .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"/>

【讨论】:

您不必将所有连接字符串移动到.env 文件。最好检查MY_CONNECTION 是否存在。如果是这样,您可以覆盖连接变量。 如果你缓存配置会有潜在的问题laravel.com/docs/5.7/… "如果你在部署过程中执行 config:cache 命令,你应该确保你只是从你的配置中调用 env 函数文件。一旦配置被缓存,.env 文件将不会被加载,所有对 env 函数的调用都将返回 null。"

以上是关于Laravel 5:处理多个连接和测试的主要内容,如果未能解决你的问题,请参考以下文章

让 Laravel 5 使用 xampp for OSX

在 Laravel 5 (Lumen) 中使用基本路径

在Laravel中创建记录后发送通知

laravel框架总结 -- blade模板引擎

Larave使用composer安装无反应,提示“Changed current directory to C:/Users/Administrator/AppData/Roaming/Compose

Larave使用composer安装无反应,提示“Changed current directory to C:/Users/Administrator/AppData/Roaming/Compose