带PHPUnit的Laravel控制器内部单元测试

带PHPUnit的Laravel控制器内部单元测试,php,unit-testing,laravel,phpunit,guzzle,Php,Unit Testing,Laravel,Phpunit,Guzzle,我不太确定在这种情况下采用哪种方法进行单元测试。对于我来说,单元测试Guzzle的示例中没有一个对如何在这个场景中实现有意义,或者可能我只是错误地看待了它 设置:Laravel 4.2 REST API-控制器方法在方法中使用Guzzle从另一个API请求数据,如下所示: <?php class Widgets extends Controller { public function index(){ // Stuff $client = new

我不太确定在这种情况下采用哪种方法进行单元测试。对于我来说,单元测试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
    }
}

?>
然而,Guzzle仍在向外部服务发出实际的HTTP请求。我的猜测可能是通过某种方式将Controller方法中的客户端创建设置为使用$handler,但我无法想象这是正确的方法。我错过了什么

编辑 我的解决方案如下:

<?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
    }
}

?>
这种解决方案感觉最正确,而且是拉雷维尔式的。()

我会在每个api调用上面添加这个(根据api调用中需要模拟的外部调用的数量更改模拟响应)

如果控制器需要Guzzle客户端,我将其添加到控制器:

public function __construct(GuzzleHttp\Client $client)
{
    $this->client = $client;
}
然后将$this->client用于所有api调用。

使用包。它有助于记录和重播HTTP请求。通过Guzzle测试api调用非常方便,对此的“经典TDD”响应是根本不应该对Guzzle进行单元测试。Guzzle是一个第三方库,它应该由自己的开发者进行充分的测试

您需要测试的是您的代码是否正确调用Guzzle,而不是当您的代码调用Guzzle时Guzzle是否工作

方法如下:

<?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
    }
}

?>
与其在控制器中执行
new Guzzle()
,不如使用依赖项注入将Guzzle对象传递到控制器中。幸运的是,拉雷维尔让这变得非常容易;您所需要做的就是为控制器类提供一个构造函数方法,并将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对象传递到类中

现在,您只需观察您的mock类,以确保对它的调用与Guzzle API期望接收到的调用相匹配


如果类的其余部分依赖于从Guzzle接收的输出,那么您也可以在模拟中定义它。

如果有人正在努力解决这个问题,那么我发现:

$this->app->bind('MyController', function($app){


在Laravel 5.5.44中为我做了这个把戏是正确的,我对测试Guzzle一点也不感兴趣,我只是希望$client->request(…)调用不被执行,只返回一些模拟数据,就好像它是成功的还是失败的——这取决于我正在进行的单元测试。今天晚些时候我要试试这个。谢谢这绝对是我认为首选的方法。这感觉最自然,因为我最终在拉雷维尔使用了IoC容器。我用我的最终答案更新了我的问题。这是一个有趣的方法。今天晚些时候我会调查的。谢谢
$this->app->bind(MyController::class, function($app){