使用PHPUnit在Laravel Controller中进行单元测试 [英] Unit Testing Guzzle inside of Laravel Controller with PHPUnit

查看:391
本文介绍了使用PHPUnit在Laravel Controller中进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不太确定在这种情况下采用哪种方法进行单元测试.在这种情况下,如何使用Guzzle进行单元测试的示例对我来说都不是很有意义,或者我只是一起看错了.

I'm not quite sure which way to approach unit testing in this scenario. None of the examples for unit testing Guzzle quite make sense to me how to implement in this scenario, or perhaps I'm just looking at it incorrectly all together.

设置:Laravel 4.2 REST API-控制器方法正在该方法中使用Guzzle从另一个api请求数据,如下所示:

The setup: Laravel 4.2 REST API - A controller method is using Guzzle in the method to request data from another api as follows:

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

?>

我认为我可以按以下方式进行单元测试,并且一切都会正常进行.

I thought that I could do my unit test as follows, and all would just work.

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方法中将Client创建设置为使用$ handler,但是我无法想象这是正确的方法.我想念什么?

However, Guzzle is still making the actual HTTP requests to the external service. My guess was maybe someway of setting the Client creation in the Controller method to use the $handler, but I can't imagine that's the right way to do it. What am I missing?

修改 我的解决方案最终如下:

Edit My solution ended up as follows:

这个解决方案感觉是最正确的,也是Laravel的方法. (请参阅IoC容器)

This solution felt the most correct, and the Laravel way. (See IoC Containers)

我会在每个api调用上方添加此代码(根据api调用中需要模拟的外部调用次数来更改模拟响应).

I would add this above each api call (change the mock responses depending on how many external calls need to be mocked in the api call).

$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客户端,则将其添加到控制器中:

And if the controller required the Guzzle client, I added this to the controller:

public function __construct(GuzzleHttp\Client $client)
{
    $this->client = $client;
}

然后将使用$ this-> client进行所有api调用.

And would then use $this->client for all of the api calls.

推荐答案

对此的经典TDD"响应是,您根本不应该对Guzzle进行单元测试. Guzzle是一个第三方库,应该由其自己的开发人员进行充分的测试.

The "classic TDD" response to this would be that you shouldn't unit test Guzzle at all. Guzzle is a third-party library which should be (and is) tested perfectly adequately by its own developer.

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

What you need to test is whether your code correctly calls Guzzle, not whether Guzzle works when your code calls it.

执行此操作的方法如下:

The way to do this is as follows:

而不是在控制器中执行new Guzzle(),而应使用依赖项注入将Guzzle对象传递到控制器中.幸运的是,Laravel使这变得非常容易.您需要做的就是为您的控制器类提供一个构造方法,并将一个Guzzle对象定义为其参数之一. Laravel将完成创建对象并将其传递给您的其余工作.然后,您的构造函数可以将其复制到类属性,以便您的其他方法可以使用它.

Rather than doing a new Guzzle() in your controller, you should instead pass a Guzzle object into your controller using dependency injection. Fortunately, Laravel makes this very easy; all you need to do is have a constructor method for your controller class, and have a Guzzle object defined as one of its arguments. Laravel will do the rest of creating the object and passing it in for you. Your constructor can then copy it to a class property so that your other methods can use it.

您的班级现在应该看起来像这样:

Your class should now look something like this:

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对象传递给您的类.

Now your test should be a lot easier to write. You can pass a mock Guzzle object into your class when you test it.

现在,您可以只观看模拟类,以确保对其的调用与Guzzle API希望收到的调用相匹配.

Now you can just watch your mock class to make sure that the calls to it match what the Guzzle API would expect to recieve in order to make the call.

如果课程的其余部分取决于从Guzzle接收到的输出,那么您也可以在模拟中对其进行定义.

If the rest of your class depends on the output received from Guzzle then you can define that in your mock as well.

这篇关于使用PHPUnit在Laravel Controller中进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆