我不太确定在这种情况下采用单元测试的方法。没有单元测试Guzzle的例子对我来说在这种情况下如何实现是非常有意义的,或者我可能只是错误地一起看它。
设置:Laravel 4.2 REST API - 控制器方法在方法中使用Guzzle从另一个api请求数据,如下所示:
<?php
class Widgets extends Controller {
public function index(){
// Stuff
$client = new GuzzleHttp\Client();
$url = "api.example.com";
$response = $client->request('POST', $url, ['body' => array(...)]);
// More stuff
}
}
?>
我以为我可以按照以下方式进行单元测试,一切都会正常工作。
function testGetAllWidgets(){
$mock_response = array('foo' => 'bar');
$mock = new MockHandler([
new Response(200, $mock_response),
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$response = $this->call('GET', '/widgets');
// Do asserts, etc.
}
但是,Guzzle仍在向外部服务发出实际的HTTP请求。我的猜测可能是在Controller方法中设置客户端创建以使用$ handler,但我无法想象这是正确的方法。我错过了什么?
编辑我的解决方案最终如下:
这个解决方案感觉最正确,而且是Laravel方式。 (See IoC Containers)
我会在每个api调用上面添加这个(根据在api调用中需要模拟多少外部调用来更改模拟响应)。
$this->app->bind('MyController', function($app){
$response_200 = json_encode(array("status" => "successful"));
$response_300 = json_encode("MULTIPLE_CHOICES");
$mock = new MockHandler([
new Response(200, [], $response_200),
new Response(300, [], $response_300)
]);
$handler = HandlerStack::create($mock);
return new MyController(new Client(['handler' => $handler]));
});
$params = array();
$response = $this->call('PUT', '/my-route', $params);
如果控制器需要Guzzle客户端,我将其添加到控制器:
public function __construct(GuzzleHttp\Client $client)
{
$this->client = $client;
}
然后将使用$ this-> client进行所有api调用。
对此的“经典TDD”回应是你不应该对Guzzle进行单元测试。 Guzzle是一个第三方库,应该由自己的开发人员完全测试(并且)。
您需要测试的是您的代码是否正确调用Guzzle,而不是Guzzle是否在代码调用时工作。
这样做的方法如下:
您应该使用依赖注入将Guzzle对象传递到控制器中,而不是在控制器中执行new Guzzle()
。幸运的是,Laravel让这很容易;你需要做的就是为你的控制器类提供一个构造函数方法,并将一个Guzzle对象定义为它的一个参数。 Laravel将完成剩余的创建对象并将其传递给您。然后,构造函数可以将其复制到类属性,以便其他方法可以使用它。
你的班级现在应该是这样的:
class Widgets extends Controller {
private $guzzle;
public function __construct(GuzzleHttp\Client $guzzle)
{
$this->guzzle = $guzzle;
}
public function index(){
// Stuff
$url = "api.example.com";
$response = $this->guzzle->request('POST', $url, ['body' => array(...)]);
// More stuff
}
}
现在你的测试应该更容易编写。您可以在测试时将模拟Guzzle对象传递到您的类中。
现在,您可以只观看您的模拟类,以确保对它的调用与Guzzle API为了拨打电话而预期接收的内容相匹配。
如果你班级的其余部分取决于从Guzzle收到的输出,那么你也可以在你的模拟中定义。
使用https://github.com/php-vcr/php-vcr包。它有助于记录和重放HTTP请求。通过Guzzle测试api调用非常方便
如果有人在努力解决这个问题,那么我找到了替换:
$this->app->bind('MyController', function($app){
同
$this->app->bind(MyController::class, function($app){
在Laravel 5.5.44中为我做了诀窍