[PHPUnit 测试] 如何使用 Fixtures 数据来处理第三方接口响应?

我被问到很多关于 API 响应测试的问题,你应该怎么做,又从哪里开始。当测试 APIs 时,我有一个通用的规则,那就是:「测试你的代码,并只测试你的代码」。这是什么意思呢? 让我解释一下:

在你的应用程序中,你正在编写与 API 集成的代码(此 API 将不受您的控制),并且你将很可能使用一些软件包库来集成此 API。因此,不要花时间去模仿和复制你无法控制的库或服务的行为,它会花费你更多的时间。相反, 把测试的重点放在你能掌控的东西上; 如何发送请求以及如何响应 API 响应。除此之外,你不能做的更多。

我之前写过关于如何在 Laravel 中集成第三方 APIs 的博客推文,所以我不打算详细描述如果集成,并只关注测试方面。想象一下我们有三个需要去集成的端点:

  • 一个 GET 端点
  • 一个 POST 端点
  • 一个 DELET 端点

这是一种典型的 CRUD 风格的行为,你想在哪里 读取 什么数据,创建 什么数据并 删除 什么数据。为此,我将使用一个虚构的 API,因为重要的是方法,而不是 API 的细节。

我们的 API

我们的 API 是一个简单的 API,让允许我们管理我们可以访问的图书馆,稀松平常的。我们可以在这个 API 中添加新书,当我们把书送给别人或以某种方式扔掉时,我们能删除它们。因此,让我们从第一个想法开始,获取我们图书馆中的书籍清单。为了简单起见,我不打算在这些示例中处理身份验证。我将使用 Laravel 的 Http facade 例子使这一点更加简单明了。

$response = Http::get('https://books-api.com/books');

完美,我们已经发出了一个请求并接收了一个响应,现在我们可以相信这个 Http facade,它已经被 Laravel 团队进行了很好的测试,因此,我们不需要去测试请求是否真的发送到了这里。我们需要做的是测试响应内容是否符合我们所预期的。在这里,使用 PestPHP 这样的库确实派上用场,因为您用于测试的语言非常容易于理解。

it('can get a list of books from the API', function () {
    $response = Http::get('https://books-api.com/books');

    expect($response->json())->toEqual('??? what goes here ???');
});

正如你从上面的实例中看到的,我们正在发送一个请求,并测试 Json 响应是否与我们可以处理的内容相同。这部分我们如何来做? 为此,我们需要打开 tests/Pest.php 并添加一个自定义的函数,让我们可以发挥一些魔力。这将允许我们加载一个 Fixture 并将它传递给 Http Facade 中以获得响应。

function fixture(string $name): array
{
    $file = file_get_contents(
        filename: base_path("tests/Fixtures/$name.json"),
    );

    if(! $file) {
        throw new InvalidArgumentException(
            message: "Cannot find fixture: [$name] at tests/Fixtures/$name.json",
        );
    }

    return json_decode(
        json: $file,
        associative: true,
    );
}

如你所见,我通常会在 tests 目录中创建一个名为 Fixtures 的目录,以便我能存储来自 API 的示例响应,以进行测试。

让我再一次演练测试代码,但是这一次我们将伪造请求并测试响应。

it('can get a list of books from the API', function () {
    $responseData = fixture('BooksApi/book-list');

    Http::fake([
        '*' => Http::response(
            body: $responseData,
            status: 200,
        ),
    ]);

    $response = Http::get('https://books-api.com/books');

    expect($response->json())->toEqual($responseData);
});

所以我们要做的是从 fixture 中拿到 json 数据,将其传递给Http::faker() 方法,以便任何请求将返回这个响应,然后预期当我们发送请求时,我们的输出就是我们所预期的。所以 Fixture 数据本身通常是从 API 文档中获取的,可能看起来有点像:

// tests/Fixtures/BooksApi/book-list.json
{
    "data": {
        [
            {
                "id": "12345",
                "title": "The Lord of The Rings",
                "author": "J R R Tolkien"
            },
            {
                "id": "12346",
                "title": "The Hobbit",
                "author": "J R R Tolkien"
            }
        ]
    }
}

因此,我们可以将以上的测试扩展更多。

it('can get a list of books from the API', function () {
    $responseData = fixture('BooksApi/book-list');

    Http::fake([
        '*' => Http::response(
            body: $responseData,
            status: 200,
        ),
    ]);

    $books = Http::get('https://books-api.com/books');

    expect($books->json())->toEqual($responseData);

    $books->json('data')->each(function ($book) {
        expect($book['author'])->toEqual('J R R Tolkien');
    });
});

因此,我们现在正在映射请求,并确保其格式符合我们的预期。

post 请求我们也可以这样做,我们可以创建一些虚假数据,并在伪造时使用Http facade发布,并测试我们的操作结果。

it('can create a new book', function () {
    $responseData = fixture('BooksApi/create-book');

    Http::fake([
        '*' => Http::response(
            body: $responseData,
            status: 201,
        ),
    ]);

    $book = Http::post('https://books-api.com/books', [
        'title' => 'Spock Must Die!',
        'author' => 'James Blish',
    ]);

    expect($books->json())->toEqual($responseData);

    expect($book->json('data'))->title->toEqual('Spok Must Die!');
});

我们现在正在测试响应是否匹配,以及当我们对数据进行交互时,属性是否与我们预期的匹配。

同样的事情也可以使用 DELETE 端点来完成,在该端点中,我们向端点发送请求并期望以特定方式格式化响应。

it('can delete a book from the API', function () {
    Http::fake([
        '*' => Http:response(
            data: null,
            status: 204,
        ),
    ]);

    $response = Http::delete('https://books-api.com/books/12345');

    expect($response->status())->toEqual(204);
});

在上面的例子我们的接口没有返回数据,因为我们刚刚删除了资源,所以我们所需要做的就是检查状态码是否匹配。除此之外,对于我们的应用程序来说,知道任何事情都不重要,我们关心的是我们请求了一个操作,并且我们从API得到了预期的答复,确认我们的操作已完成或至少已监听。

API测试并不困难,您可以对其进行深入的了解,但是您只需要深入到应用程序所关心的范围。超出这个范围将浪费您用于代码其他领域的宝贵时间。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://www.juststeveking.uk/testing-api...

译文地址:https://learnku.com/laravel/t/67993

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!