如何动态更改具体的类绑定。
我正在尝试测试使用外部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);
}
是否可以通过这种方式实现欲望行为?还是服务容器绑定在请求生存期内无法更改?
答案 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);
}
}