spurjobs/spectator

此包已被废弃且不再维护。作者建议使用 https://github.com/hotmeteor/spectator 包。

测试您的 OpenAPI 规范的辅助工具

v2.0 2024-03-19 11:57 UTC

README

68747470733a2f2f737065637461746f722e73332e75732d656173742d322e616d617a6f6e6177732e636f6d2f737065637461746f722d6c6f676f2e706e67

Spectator

Spectator 提供轻量级的 OpenAPI 测试工具,您可以在现有的 Laravel 测试套件中使用。

编写测试以验证您的 API 规范不偏离实现。

Tests Latest Version on Packagist PHP from Packagist

要求

  • PHP 8.1+
  • Laravel 10+

安装

您可以通过 Composer 安装此包。

composer require hotmeteor/spectator --dev

然后,使用以下命令发布此包的配置文件

php artisan vendor:publish --provider="Spectator\SpectatorServiceProvider"

配置文件将发布在 config/spectator.php

从 v1 升级到 v2

重要: Spectator v2 需要 PHP 8.1 和 Laravel 10。如果您正在使用较旧的 PHP 或 Laravel 版本,则不应升级到 v2。

虽然这通常是一个直接的升级,但您应该注意一些所做的更改。

请阅读 UPGRADE.md 文件以获取更多信息。

配置

来源

来源 是您 API 规范所在位置的引用。根据您或您的团队的工作方式,或者规范所在的位置,您可能希望为不同的环境配置不同的来源。

如配置所示,有三种来源类型可用:localremotegithub。每个来源都需要定义您的规范所在的文件夹,而不是规范文件本身。这为在单个项目中处理多个 API 或 API 拆分到多个规范文件中提供了灵活性。

本地

## Spectator config

SPEC_SOURCE=local
SPEC_PATH=/spec/reference

远程

这是使用来自 Github 的原始访问链接,但可以指定任何远程来源。可以使用 SPEC_URL_PARAMS 来追加远程 URL 所需的任何附加参数。

## Spectator config

SPEC_PATH="https://raw.githubusercontent.com/path/to/repo"
SPEC_URL_PARAMS="?token=ABEDC3E5AQ3HMUBPPCDTTMDAFPMSM"

GitHub

这使用 Github 个人访问令牌,允许您访问包含您的合同的远程仓库。

您可以从 此链接 了解如何获取您的个人访问令牌。

重要的是要记住,SPEC_GITHUB_PATH 必须包含分支(例如:main)以及包含您的合同的目录的路径。

## Spectator config

SPEC_GITHUB_PATH='main/contracts'
SPEC_GITHUB_REPO='orgOruser/repo'
SPEC_GITHUB_TOKEN='your personal access token'

指定目标规范文件

在您的测试中,您将声明您想要测试的规范文件

public function testBasicExample()
{
    Spectator::using('Api.v1.json');

    // ...

测试

范式转变

现在,让我们谈谈好的东西。

最初,规范测试或契约测试可能看起来有些反直觉,尤其是在与 Laravel 支持的“功能”或“功能”测试相比时。

虽然“功能”测试确保您的请求验证、控制器行为、事件、响应等在人们与您的 API 交互时按预期行为,但“契约”测试确保 请求和响应符合规范 —— 仅此而已。数据本身可能不正确,但这超出了契约测试的范围。

编写测试

Spectator 引入了一些简单的工具来补充现有的 Laravel 测试工具箱。

以下是一个典型的 JSON API 测试示例

<?php

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJson([
                'created' => true,
            ]);
    }
}

以下是一个契约测试示例

<?php

use Spectator\Spectator;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     *
     * @return void
     */
    public function testBasicExample()
    {
        Spectator::using('Api.v1.json');

        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertValidRequest()
            ->assertValidResponse(201);
    }
}

该测试验证请求和响应是否符合规范,本例中规范位于Api.v1.json。这种类型的测试促进了TDD:您首先可以为端点编写端点合同测试,然后确保规范和实现是一致的。

在您的规范中,应该对每个可能响应进行记录。例如,单个POST端点可能导致2xx4xx或甚至5xx的响应代码。此外,您的端点可能还有特定的参数验证需要遵守。

这就是合同测试与功能测试不同的地方

  • 功能测试中,测试成功和失败的响应结果
  • 合同测试中,测试请求和响应的符合性,结果并不重要。

调试

对于某些验证错误,会抛出一个特殊的异常消息,显示错误消息(s)以及预期的模式。例如

  ---

The properties must match schema: data
All array items must match schema
The required properties (name) are missing

object++ <== The properties must match schema: data
    status*: string
    data*: array <== All array items must match schema
        object <== The required properties (name) are missing
            id*: string
            name*: string
            slug: string?

  ---

使用了一些自定义符号

  • "++": 对象支持 additionalProperties
  • "*": 项是 required
  • "?": 项可以是 nullable

用法

提供规范

定义测试要使用的规范文件。这可以在您的setUp()方法或特定的测试方法中定义。

<?php

use Spectator\Spectator;

class ExampleTest extends TestCase
{
    public function setUp(): void
    {
        parent::setUp();

        Spectator::using('Api.v1.json');
    }

    public function testApiEndpoint()
    {
        // Test request and response...
    }

    public function testDifferentApiEndpoint()
    {
        Spectator::using('Other.v1.json');

        // Test request and response...
    }
}

测试请求

在测试端点时,有一些新的方法

$this->assertValidRequest();
$this->assertValidResponse($status = null);
$this->assertValidationMessage('Expected validation message');
$this->assertErrorsContain('Check for single error');
$this->assertErrorsContain(['Check for', 'Multiple Errors']);

当然,您仍然可以继续使用所有现有的HTTP测试方法

$this
    ->actingAs($user)
    ->postJson('/comments', [
        'message' => 'Just over here spectating',
    ])
    ->assertCreated()
    ->assertValidRequest()
    ->assertValidResponse();

但是,混合功能测试和合同测试可能后来更难管理和阅读。强烈建议将两种类型的测试分开。

测试响应

您可以使用内置的->assertStatus($status)方法,也可以验证有效的响应是否实际上是您要检查的响应。例如,您可能从单个端点接收到200202,您想确保您正在验证正确的响应。

$this
    ->actingAs($user)
    ->postJson('/comments', [
        'message' => 'Just over here spectating',
    ])
    ->assertValidRequest()
    ->assertValidResponse(201);

当抛出非特定于此包目的的异常时,例如拼写错误或缺少导入,输出将默认使用相当简短的消息,并且没有堆栈跟踪。可以通过禁用Laravel内置的验证处理程序来更改此设置,以在运行测试时更容易进行调试。

这可以通过几种不同的方式完成

class ExampleTestCase
{
    public function setUp(): void
    {
        parent::setUp();

        Spectator::using('Api.v1.json');

        // Disable exception handling for all tests in this file
        $this->withoutExceptionHandling();
    }

    // ...
}
class ExampleTestCase
{
    public function test_some_contract_test_example(): void
    {
        // Only disable exception handling for this test
        $this->withouthExceptionHandling();

        // Test request and response ...

    }
}

禁用Spectator

如果您想为特定的测试禁用Spectator,可以使用Spectator::reset方法

<?php

use Spectator\Spectator;

class ExampleTest extends TestCase
{
    public function setUp(): void
    {
        parent::setUp();

        Spectator::using('Api.v1.json');
    }

    public function testWithoutSpectator()
    {
        Spectator::reset();
        
        // Run your test without Spectator
    }
}

核心概念

方法

Spectator通过注册一个自定义中间件来实现,该中间件会对规范执行请求和响应验证。

依赖关系

对于有兴趣为Spectator做出贡献的人,熟悉用于规范测试的核心依赖项很有用

  • cebe/php-openapi:用于将规范解析成可用的数组
  • opis/json-schema:用于执行对象/数组对规范的验证

赞助商

非常感谢所有赞助商,他们的帮助推动了Spectator的发展!

如果您想成为赞助商,请在此了解更多信息。💪

鸣谢

68747470733a2f2f636f6e747269622e726f636b732f696d6167653f7265706f3d686f746d6574656f722f737065637461746f72

contributors-img制作。

许可证

麻省理工学院许可证(MIT)。请参阅许可证文件获取更多信息。