LaravelアプリのHTTP API実装がOpenAPI Spefication v3のドキュメントに準拠しているかどうかをテストする
こんにちは、ナカエです。 スタッフブログも随分と間が空いてしまいました。
以前、LaravelでHTTP API実装がOpenAPI Specification v2(Swagger)のドキュメントに準拠しているかどうかをテストする方法についての記事が投稿されていました。
本日はOpen API Specification v3に沿って記述されたドキュメントを利用して同様にテストを実行する方法についての記事です。
OpenAPI Specification v3
OAS v3ではSwaggerからOpenAPI Specificationに名称変更されるとともに、様々な変更がなされています。
- 構造の単純化
- 複数のサーバ定義のサポート
- コンテンツネゴシエーションのサポート
- Securityの定義の強化
- oneOf、anyOf、notなどのJSON Schemaの機能のサポート
- Link、Callbackなど新機能
3.0.0のリリース以降もバージョンアップを重ねており、最新は3.1.0となっています。 Google CloudのCloud EndpointsやAPI Gatewayのように未だOAS v2のみの対応となっているサービスもありますが、そういった事情がなければ3系を使うほうが柔軟です。
ドキュメントファースト
本記事は以前の記事に倣い、バックエンドのソースコードからOASのドキュメントを生成する手法ではなく、OASのドキュメントに実装が従っているかをテストする手法を扱います。
Symfony HTTP FoudationのHTTPメッセージのバリデーション
OAS v3用のバリデータライブラリを利用します。
こちらはPSR-7 HTTP MessageのRequest/Response用のライブラリとなっているので、そのままではSymfony HTTP FoundationのRequest/Responseクラスに対しては使えません。そこで、
を用いてPSR-7のHTTPメッセージとHTTP FoundationのHTTPメッセージを変換することにします。
主眼ではないので詳細は割愛しますが、例えば下記のようにHTTP Foundation用のOpenAPIバリデータライブラリを構成できます。
n1215/openapi-http-foundation-validator
元のleague/openapi-psr7-validatorのクラスを薄くラップした構成になっており、キャッシュとしてPSR6以外にPSR16 Simple Cacheが利用できる以外は特筆すべきところはありません。
使い方
/** @var \N1215\OpenApiValidation\HttpFoundation\ValidatorBuilder $validatorBuilder */
$validatorBuilder = (new \N1215\OpenApiValidation\HttpFoundation\ValidatorBuilder($psr17Factory))
->fromJsonFile('/path/to/openapi.json')
->setSimpleCache(new YourPsr16Cache(), 3600);
// バリデータを取得
/** @var \N1215\OpenApiValidation\HttpFoundation\Validators $validators */
$validators = $validatorBuilder->getValidators();
// Requestをバリデーション
/** @var \Symfony\Component\HttpFoundation\Request $request */
/** @var \N1215\OpenApiValidation\HttpFoundation\RequestValidatorInterface $requestValidator */
$requestValidator = $validators->getRequestValidator();
$requestValidator->validate($request);
// Resopnseをバリデーション
/** @var \Symfony\Component\HttpFoundation\Response $response */
/** @var \N1215\OpenApiValidation\HttpFoundation\ResponseValidatorInterface $responseValidator */
$responseValidator = $validators->getResponseValidator();
$responseValidator->validate(
new \N1215\OpenApiValidation\OperationAddress('/path', 'GET'),
$response
);
Laravel用のパッケージ
Laravelが利用するIlluminateのHTTPメッセージはSymfony HTTP FoundationのHTTPメッセージを継承しているため、上記バリデータをLaravelのRequest/Responseに対して直接使うことが可能です。 しかし、テスト用に使いまわすとなるとユーティリティが欲しくなってきます。
そこでLaravel用の便利パッケージも作りました。
上記バリデータとlaravel/frameworkへの依存に加え、PSR-7のライブラリとして、nylolm/psr7 を利用しています。
インストール
Composerを使ってインストールできます。
composer require n1215/openapi-laravel-validator
使い方
1. サービスプロバイダの作成
サービスプロバイダでValidatorBuilderの生成を上書きしconfig/app.phpに追加します。定義ファイルのパスはプロジェクトのディレクトリ構成に応じて変更してください。
キャッシュはテスト実行時間に影響があるため設定を推奨しますが、元のOAS定義ファイルを変更してもテストに反映されない場合があるためテスト実行前のキャッシュのクリア php artisan cache:clear
を忘れないよう注意が必要です。
<?php
declare(strict_types=1);
namespace App\Providers;
use Illuminate\Support\Facades\Cache;
use N1215\OpenApiValidation\HttpFoundation\ValidatorBuilder;
use N1215\OpenApiValidation\Laravel\OpenApiLaravelValidatorServiceProvider;
class OpenApiValidatorServiceProvider extends OpenApiLaravelValidatorServiceProvider
{
protected function makeValidationBuilder(): ValidatorBuilder
{
$httpMessageFactory = $this->makeHttpMessageFactory();
return (new ValidatorBuilder($httpMessageFactory))
->fromYamlFile('/path/to/your-definition.yaml')
->setSimpleCache(Cache::store(), 3600);
}
}
2. AssertsWithOpenApiトレイトをテストで利用
setOpenApiAssertion()メソッドによりテスト対象となるAPI定義を宣言し、その後に該当のAPIをコールすると、検証が自動で実施され、エラーがある場合はテストが失敗します。
バリデーションエラーのテストを書く場合などリクエストの検証をスキップしたい場合もあるため、disableRequestAssertion()メソッドを使って検証を無効化できるようになっています。
<?php
declare(strict_types=1);
namespace Tests\Feature;
use Tests\TestCase;
use N1215\OpenApiValidation\Laravel\Test\AssertsWithOpenApi;
class GetHelloTest extends TestCase
{
use AssertsWithOpenApi;
public function testSuccess(): void
{
$this->setOpenApiAssertion(
'/hello',
'get'
);
$response = $this->json(
'get',
'/hello?name=Taro'
);
$response->assertOk();
$response->assertJson(['message' => 'Hello, Taro']);
}
public function testValidationFailed(): void
{
$this->setOpenApiAssertion(
'/hello',
'get'
);
// リクエストパラメータが不正になるため、リクエストの検証を無効化する
$this->disableRequestAssertion();
$response = $this->json(
'get',
'/hello'
);
$response->assertStatus(422);
$response->assertJsonValidationErrors(['name' => 'The name field is required']);
}
}
まとめ
PSR7 HTTPメッセージ用のバリデータとSymfonyのPSRブリッジを用いて、LaravelアプリのHTTP APIテスト時にOASへの準拠を検証するパッケージを作成しました。