使用PHPUnit对Laravel控制器内部进行单元测试

问题描述 投票:5回答:3

我不太确定在这种情况下采用单元测试的方法。没有单元测试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调用。

php unit-testing laravel phpunit guzzle
3个回答
6
投票

对此的“经典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收到的输出,那么你也可以在你的模拟中定义。


2
投票

使用https://github.com/php-vcr/php-vcr包。它有助于记录和重放HTTP请求。通过Guzzle测试api调用非常方便


0
投票

如果有人在努力解决这个问题,那么我找到了替换:

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

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

在Laravel 5.5.44中为我做了诀窍

© www.soinside.com 2019 - 2024. All rights reserved.