根据需要在Laravel Service Container中更改具体的类绑定

时间:2019-05-28 06:46:54

标签: laravel inversion-of-control artisan

如何动态更改具体的类绑定。

我正在尝试测试使用外部API的工匠命令。

class ConsumeApiCommand extends Command
{
    public function __construct(ClientInterface $client)
    {
        parent::__construct();

        $this->client = $client;
    }

    public function handle()
    {
        $api_response = $this->client->request('POST', 'http://external.api/resource');

        $response = json_decode($api_response);

        if(isset($response['error'])) {
            $this->error($response['error']);
        } else {
            $this->status($response['status']);
        }
    }
}

当前;我可以在测试中伪造具体的课程。

class FakeServiceProvider extends AppServiceProvider
{
    public function register(): void
    {
        $this->app->bind(ClientInterface::class, function () {
            return new class implements ClientInterface {
                public function request($method, $uri, $headers = [], $body = [])
                {
                    return json_encode(['status' => "You've reached us."]);
                }
            };
        });
    }
}

通过。

public function test_can_consume_api_if_authenticated()
{
    $this->artisan('consume:api')
         ->expectsOutput("You've reached us.")
         ->assertExitCode(0);
}

失败;返回最初绑定的类响应You've reached us.

public function test_cant_consume_api_if_not_authenticated()
{
    $this->app->bind(ClientInterface::class, function () {
        return new class implements ClientInterface {
            public function request($method, $uri, $headers = [], $body = [])
            {
                return json_encode(['error' => "Unauthorized."]);
            }
        };
    });

    $this->artisan('consume:api')
         ->expectsOutput("Unauthorized.")
         ->assertExitCode(0);
}

是否可以通过这种方式实现欲望行为?还是服务容器绑定在请求生存期内无法更改?

2 个答案:

答案 0 :(得分:1)

始终允许将新的具体类绑定到Laravel中的接口。 我在这里看到的问题(过去也曾遇到过类似的情况)是,当您bind()新的具体类时,artisan命令已经被初始化(使用旧的绑定)。

在测试用例的setUp()方法中,您应该重新注册该命令,以便它将采用来自接口的新绑定。

要测试这是否可行,只需在命令的dump()方法中添加__construct(),然后在测试的setUp中添加另一个。如果我是正确的话,您应该看到che命令的第一个出现,然后另一个出现。

答案 1 :(得分:0)

我只是为那些可能想知道我如何解决此问题的人留下了一段代码。

基于Leonardo's answer,问题的源还可以,但是我需要重新注册命令才能选择新的绑定。

use Illuminate\Contracts\Console\Kernel;

class ConsumeApiCommandTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        // Re-bootstrap console kernel.
        $this->app->make(Kernel::class)->bootstrap();
    }

    public function test_cant_consume_api_if_not_authenticated()
    {
        $this->app->bind(ClientInterface::class, function () {
            return new class implements ClientInterface {
                public function request($method, $uri, $headers = [], $body = [])
                {
                    return json_encode(['error' => "Unauthorized."]);
                }
            };
        });

        // Artisan command now using newly binded concrete class.

        $this->artisan('consume:api')
             ->expectsOutput("Unauthorized.")
             ->assertExitCode(0);
    }
}