创建可进行单元测试的 API 客户端


Creating an API client that's unit testable

我目前正在构建一个简单的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)

我测试这个的最佳方式是什么?

通常,

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

我看到两个选项:

  1. 使用不同的、更易于测试的库,如 Guzzle
  2. 如果这是不可能的,请编写一个薄包装器,如下所示:

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

    然后将此实例传递给 ApiClient 构造函数,并在测试中模拟它。