Rails + OpenAPI で作られているAPIのテストコードを書く時、
- interagent/committee: A collection of Rack middleware to support JSON Schema.
- willnet/committee-rails: rails and committee are good friends
を使うことで、OpenAPIスキーマのテストもしやすいので便利です。
ただ、レスポンスボディがないAPIの場合、committee の assert_response_schema_confirm()
でエラーになったため、メモを残します。
目次
環境
作成したAPI
rails new rails_committee_app --api
のようにAPIモードで作成したAPIアプリに対し、こんな感じのRails Controllerがあったとします。
# app/controllers/fruits_controller.rb class FruitsController < ApplicationController def index render json: { fruits: [{ name: 'りんご' }, { name: 'みかん' }]} end def create # 処理したつもり head :created end end
このControllerのレスポンスは
index
メソッドの場合、fruits のリストが返るcreate
メソッドの場合、何も返さない
となります。
また、ルーティングのところで、Controllerの
index
メソッドはGETcreate
はPOST
にそれぞれ対応させているとします。
# confing/routes.rb Rails.application.routes.draw do resources :fruits, only: %i[index create] end
また、OpenAPIスキーマは
openapi: 3.0.0 info: title: Rails API with committee description: committee を使ったRails APIです version: 0.0.1 servers: - url: http://localhost:7401 description: development tags: - name: fruits description: 果物 components: schemas: Fruits: type: object properties: fruits: type: array items: $ref: '#/components/schemas/Fruit' Fruit: type: object properties: name: description: 名前 type: string example: りんご paths: /fruits: get: summary: 果物一覧 tags: - fruit responses: '200': description: 取得成功 content: application/json: schema: $ref: '#/components/schemas/Fruits' post: summary: 果物の登録 tags: - fruit requestBody: content: application/json: schema: $ref: '#/components/schemas/Fruit' required: true responses: '201': description: 登録成功
だったとします。
curlで動作確認をすると、GETの場合は
% curl -X 'GET' -H "Content-Type: application/json" 'http://localhost:7401/fruits/' {"fruits":[{"name":"りんご"},{"name":"みかん"}]
POSTの場合は
% curl -H "Content-Type: application/json" -d '{"name": "バナナ"}' 'http://localhost:7401/fruits' -v * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 7401 (#0) > POST /fruits HTTP/1.1 > Host: localhost:7401 > User-Agent: curl/7.64.1 > Accept: */* > Content-Type: application/json > Content-Length: 21 > * upload completely sent off: 21 out of 21 bytes < HTTP/1.1 201 Created
と、GETとPOSTとも正常に動作しています。
テストコード
次に、このAPIに対してテストを書くことにしました。
エラーとなる例
rspecにて
require 'rails_helper' RSpec.describe 'FruitsController', type: :request do let(:request_headers) { { 'Content-Type' => 'application/json', 'accept' => 'application/json' } } describe '#index' do it '200' do get "/fruits", headers: request_headers assert_request_schema_confirm assert_response_schema_confirm(200) end end describe '#create' do let(:request_params) { { 'name': "apple" }.to_json } it 'error by assert_response_schema_confirm' do post '/fruits', params: request_params, headers: request_headers assert_request_schema_confirm assert_response_schema_confirm(201) end end end
というテストコードを書きました。
次にテストを実行したところ、 it 'error by assert_response_schema_confirm'
のテストケースで
Committee::InvalidResponse: #/paths/~1fruits/post/responses/201 response definition does not exist
というエラーが発生し、テストが失敗しました。
対応例
OpenAPIのスキーマについて、POSTのところを見ると、
paths: /fruits: post: ... responses: '201': description: 登録成功
Controllerでは何もレスポンスを返さないため、OpenAPIのスキーマ的には正しいです。
ただ、 committee の assert_response_schema_confirm
は「OpenAPIのスキーマにHTTPレスポンスボディが存在する時に使う」ことを前提にしているのかもしれません。
そこで、そもそもPOSTの場合はレスポンスボディの検証は不要なことから
it '201' do post '/fruits', params: request_params, headers: request_headers assert_request_schema_confirm # HTTPステータスコードを確認するだけ expect(response.status).to eq 201 end
とHTTPステータスコードだけを確認するようにしたところ、テストがパスしました。
ソースコード
Githubに上げました。
https://github.com/thinkAmi-sandbox/rails_committee-sample