RspecでAPIが正しく動作するかテストする

こんにちは!kossyです!




さて、今回は、RspecのRequestSpecでAPIが正しく値を返すかどうかをテストする方法を
ブログに残してみたいと思います。




環境
Rails 5.2.3
Ruby 2.5.1
devise_token_auth
factorybot
rspec
rack_cors
jbuilder





参考実装(Spec以外)

コントローラやビューは以下のようになってます。
RailsAPIモードで作成し、jsonを返すAPIに徹する想定です。

app/controllers/admins_controller.rb

class AdminsController < ApplicationController
  before_action :authenticate_admin!

  def index
    @admins = Admin.all
  end

end
app/views/index.json.jbuilder

json.array! @admins, partial: 'admins/admin', as: :admin



app/views/_admin.json.jbuilder

json.extract! admin, :id,  :email, :last_name, :first_name

参考実装(テストコード)

Factorybotも含めたテストコードは以下のようになってます。

spec/factories/admins.rb

FactoryBot.define do
  factory :admin do
    provider   'email'
    uid        { Faker::Internet.safe_email }
    password   { Faker::Internet.password }
    email      { uid }
    last_name  { Faker::Japanese::Name.last_name }
    first_name { Faker::Japanese::Name.first_name }
  end
end
spec/supports/login.rb

このモジュールはrails_helper.rbでincludeする。

config.include Login



module Login

  def login(member)
    @member = member
    @auth_token = @member.create_new_auth_token
  end

  def logout
    @member = nil
    @auth_token = nil
  end

  def current_member
    @member
  end

  def default_header
    if @auth_token
      {
        'access-token' => @auth_token['access-token'],
        'client' => @auth_token['client'],
        'expiry' => @auth_token['expiry'],
        'uid' => @auth_token['uid'],
        'Accept' => 'application/json',
        'Content-Type' => 'application/json'
      }
    else
      {
        'Accept' => 'application/json',
        'Content-Type' => 'application/json'
      }
    end
  end

  def upload_header
    if @auth_token
      {
        'access-token' => @auth_token['access-token'],
        'client' => @auth_token['client'],
        'expiry' => @auth_token['expiry'],
        'uid' => @auth_token['uid'],
        'Accept' => 'application/json',
        'Content-Type' => 'multipart/form-data'
      }
    else
      {
        'Accept' => 'application/json',
        'Content-Type' => 'multipart/form-data'
      }
    end
  end

  def get(url, params=nil, header={})
    super url, params: params, headers: header.merge(default_header)
  end

  def post(url, params=nil, header={})
    if params.is_a?(Hash) && params.key?(:files)
      super url, params: params, headers: header.merge(upload_header)
    else
      super url, params: params.try(:to_json), headers: header.merge(default_header)
    end
  end

  def patch(url, params=nil, header={})
    super url, params: params.try(:to_json), headers: header.merge(default_header)
  end

  def delete(url, params=nil, header={})
    super url, params: params, headers: header.merge(default_header)
  end
end




>|ruby|
spec/requests/admins_spec.rb

require 'rails_helper'

RSpec.describe 'Admin ', type: :request do
  describe 'Admin' do
    describe 'GET /admins' do
      it 'Admin index' do
        admin = create :admin
        login admin

        get '/admins'

        expect(response.status).to eq 200
        json = JSON.parse(response.body)
        expect(json.size).to eq 1
        admin_json = json[0]
        expect(admin_json['id']).to eq admin.id
        expect(admin_json['email']).to eq admin.email
        expect(admin_json['last_name']).to eq admin.last_name
        expect(admin_json['first_name']).to eq admin.first_name
      end
    end
  end
end

まずはレスポンスステータスコードが200であることを確かめて、
responseをjsonにparseします。

その後、jsonのsizeが1つであることを確認して、
jsonの先頭をadmin_jsonとして定義します。

あとはそれぞれのkeyとfactoryBotで作ったadminの属性と合っているかどうかをチェックします。