如何仅在 phpunit 测试中更改硬编码的 Eloquent $connection?

lew*_*s4u 5 phpunit laravel

我有一个像这样的雄辩模型:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;


class SomeModel extends Model
{

    protected $connection = 'global_connection';

......................
Run Code Online (Sandbox Code Playgroud)

问题是这个 $connection 必须是硬编码的,因为我有一个多租户网络平台,所有租户都应该从这个数据库中读取。

但是当现在在测试中时,我正在store()访问Controller 路由,而我无权访问模型!我只是这样做:

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

    return response()->json(['msg' => 'Success']);
}
Run Code Online (Sandbox Code Playgroud)

当用户通过浏览器使用它时效果很好......

但是现在我想以某种方式强制该模型不使用硬编码的 $connection 并将其设置为测试数据库连接...

这是我的测试

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

    $response = $this->postJson($this->route, $attributes)->assertSuccessful();
}
Run Code Online (Sandbox Code Playgroud)

有没有什么办法可以用一些 Laravel 魔法来实现这一点:)?

lew*_*s4u 2

不幸的是,对我来说,由于我针对多租户的特定数据库设置,这些答案都没有达到目的。我得到了一些帮助,这是解决这个问题的正确方法:

在 laravel 中的tests/目录下创建一个自定义类ConnectionResolver

<?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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在测试中像这样使用它

在tests/TestCase.php中创建一个名为create()的方法

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;
}
Run Code Online (Sandbox Code Playgroud)

在实际测试中你可以简单地这样做:

/** @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);
}
Run Code Online (Sandbox Code Playgroud)

$this->注意:使用 setUp() 方法和符号时,测试方法只能有一行

就是这样。这样做的作用是强制自定义连接名称(应写入 config/database.php 中),并且无论您在模型中指定什么,调用期间的模型都将使用该连接,因此它将数据存储到数据库中您已指定$model::setConnectionResolver(new ConnectionResolver($original, 'HERE'));