我有一个Laravel 5.4应用程序,它的模型指向不同的数据库连接。
例如,我有User
指向MySQL数据库,然后Company
指向PostgreSQL数据库(使用$connection
变量)。
现在,当我运行PHPUnit时,我希望将$connection
变量替换为phpunit.xml
文件中指定的内容,该文件是内存类型数据库中的SQLite。
这怎么可以实现?
答案 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