正确测试调用 API 的 SDK

Properly testing an SDK that calls an API

我已经编写了一个 API,现在我正在为第 3 方编写一个 SDK,以便更轻松地与我的 API 进行交互。

在为我的 SDK 编写测试时,我的理解是最好不要简单地调用所有 API 端点,因为:

  1. API 中的测试将负责确保 API 正常工作。
  2. 如果 SDK 测试直接调用 API,我的测试会很慢。

举个例子,假设我的 API 有这个端点:

/account

在我的 API 测试套件中,我实际上调用了这个端点来验证它 returns 正确的数据。

我在我的 SDK 中采用什么方法对此进行测试?我应该嘲笑 /account 的请求吗?我还需要做什么才能使我的 SDK 得到良好的覆盖?

我查看了其他 SDK(Stripe、Algolia、AWS),看看它们是如何处理这个问题的,但在某些情况下,它们看起来确实在调用实际 API.

(我目前正在使用 PHPUnit,但我也会用其他语言编写 SDK。)

在为 SDK 编写测试时,您假设您的 api 确实按预期工作(并且您为 api 编写测试以确保这一点)。

因此,使用某种沙箱甚至是 api 的完整模拟就足够了。

我建议使用 wiremock 之类的东西来模拟您的 API,然后围绕该模拟 API 编写您的单元测试,以确保一切正常。

这样,当您的生产应用程序出现故障时,您至少可以确保(通过 运行 单元测试)您的应用程序方面没有任何故障,但实际 API 可能存在问题(即响应格式正在更改)。

我最终采用了这种方法:

我有两个单元测试和集成测试。

我的集成测试调用实际 API。我通常 运行 这种频率要低得多——就像在我将代码推送到远程之前一样。 (任何使用我的代码的人都必须提供他们自己的 API 凭据)

我的单元测试 - 我 运行 非常频繁 - 只是确保我的代码的响应是我期望的样子。我相信第 3 方 API 会给我很好的数据(而且我仍然有集成测试来支持它)。

我已通过 mocking Guzzle, using Reflection to replace the client instance in my SDK code, and then using Mock Handlers 完成此操作以模拟我期望的实际响应。

这是一个例子:

/** @test */
public function it_retrieves_an_account()
{
    $account = $this->mockClient()->retrieve();

    $this->assertEquals(json_decode('{"id": "9876543210"}'), $account);
}

protected function mockClient()
{
    $stream = Psr7\stream_for('{"id": "9876543210"}');
    $mock = new MockHandler([new Response(
        200,
        ['Content-Type' => 'application/json'],
        Psr7\stream_for($stream)
    )]);

    $handler = HandlerStack::create($mock);
    $mockClient = new Client(['handler' => $handler]);

    $account = new SparklyAppsAccount(new SparklyApps('0123456789'));
    $reflection = new \ReflectionClass($account);
    $reflection_property = $reflection->getProperty('client');
    $reflection_property->setAccessible(true);
    $reflection_property->setValue($account, $mockClient);

    return $account;
}