Rails + OpenAPI なAPIにて、レスポンスボディがない場合、committee の assert_response_schema_confirm を使うとエラーになる

Rails + OpenAPI で作られているAPIのテストコードを書く時、

を使うことで、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 メソッドはGET
  • create は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