【问题标题】:Creating an API client that's unit testable创建可单元测试的 API 客户端
【发布时间】:2016-01-16 15:05:45
【问题描述】:

我目前正在构建一个简单的 API 客户端,它将为远程服务的 HTTP API 提供一个很好的包装器。我无法弄清楚如何在我的客户端中模拟 HTTP 响应,以使其易于测试。

以下面的示例类为例:

<?php

class ApiClient
{
    const API_BASEURL = 'https://api.someservice.com/v1';

    protected $applicationId;
    protected $secretKey;

    public function __construct($applicationId, $secretKey)
    {
        $this->applicationId = $applicationId;
        $this->secretKey = $secretKey;
    }

    public function listAccounts()
    {
        $response = \Httpful\Request::get(self::API_BASEURL.'/accounts')
            ->expectsJson()
            ->send();

        return $response->accounts;
    }
}

我希望使用我的客户端的开发人员能够像这样使用它:

$client = new ApiClient($applicationId, $secretKey);
$accounts = $client->listAccounts();

在实践中,这工作得很好。但是我真的很难对listAccounts() 方法进行单元测试,因为它将HTTP 请求发送到硬编码的API_BASEURL。显然,我不希望我的测试真正触发对远程服务的网络调用。

我想解决这个问题的一种方法是模拟 \Httpful\Request 类,使其返回“伪造”响应而不是实际访问网络。但是如何将模拟类传递给我的 listAccounts() 方法?为了测试,我不能将其签名更改为listAccounts($mockedRequest = null)

对我来说,最好的测试方法是什么?

【问题讨论】:

    标签: php unit-testing testing phpunit


    【解决方案1】:

    通常你应该将请求对象注入到类中(通过构造函数参数传递它)并移除硬依赖。但是在您的情况下,没有可以用模拟替换的请求对象。 这就是静态方法的问题。如果你想编写单元测试,这个 Httpful 库似乎不是一个好的选择。

    我看到两个选项:

    1. 使用不同的、对测试更友好的库,例如Guzzle
    2. 如果不可能,请编写一个瘦包装器,如下所示:

      class HttpfulClient
      {
          public function createGetRequest($url)
          {
              return \Httpful\Request::get($url);
          }
      }
      

      然后将 this 的一个实例传递给 ApiClient 构造函数并在您的测试中模拟它。

    【讨论】:

    • 我改用 Guzzle,因为它允许我将模拟响应排队。然后我可以选择通过其构造函数将模拟的 Guzzle 客户端传递给我的 API 客户端。到目前为止似乎运作良好!
    猜你喜欢
    • 2015-06-28
    • 1970-01-01
    • 2020-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-07
    • 2012-07-30
    • 1970-01-01
    相关资源
    最近更新 更多