🎭

【WebMock+RSpec】テストコードで外部APIを模倣する【余分なAPIコストを抑制】

2024/04/09に公開

はじめに

個人開発中のアプリケーションのテストコードを実装する際、テストを行うたびに実際の外部API(画像生成API)を叩くことになるため主に以下の2点の課題を抱えていました。

  • レート制限(429エラー)に達してしまいレスポンスが安定しない
  • 従量課金型のAPIを導入した場合、テストの度に課金が発生する

これらの課題を解消するにはAPIのレスポンスを模倣(エミュレート)し予め用意した擬似データをAPIレスポンスとして返却する処理が必要です。
これを実現するのが WebMock というGemです。
テストの安定性を向上させることができ、延いては従量課金APIの場合はテストの度に課金されてしまうことも抑制できます。(コスト削減!)

私の開発ケースで、実装までの工程を紹介したいと思います。

概要

まず、WebMockの概要ですが、 stub_request によって指定した外部へのHTTPリクエスト(今回のケースだと APIエンドポイントURL )を捕捉してあらかじめ設定したモック(偽レスポンス)を返すために使用されます。
つまり、 指定したURLと一致するURLに外部アクセスを試みた瞬間にWebMockくんが横入りしてきてあらかじめ用意した模倣データを返してくれる という流れです。

公式ドキュメントは以下
https://github.com/bblimke/webmock

実装手順

  1. webmock をインストールします。
    group :test do〜end のブロックに記述。
Gemfile
group :test do
# 〜省略〜
  gem 'webmock'
end
ターミナル
bundle install
  1. WebMockの初期設定 をします。
    WebMockはデフォルト設定だとスタブ登録(模倣登録)していないAPIのアクセスを許可しません。←これがややこしい
    つまり、模倣しなくてもよい外部リクエストがあった場合でもスタブ登録(模倣登録)しないとアクセスがブロックされ、エラーが返りテストをパスできません。(←わかりづらかったらすみません。要はテスト中に存在する外部リクエストを 全部スタブ登録してモック(模倣データを準備)するか、下記のコードを追加してスタブ登録してないAPIのアクセスをブロックされないようにするか です。)
    私のケースでは自作した無料APIへのリクエストは模倣する必要がない(通常どおりAPIリクエストを行っても課金の心配もない)ため スタブ化していないAPIへのアクセスを許可する記述 を追加します。
spec/rails_helper.rb
require 'webmock'
WebMock.allow_net_connect!(:net_http_connect_on_start => true)
  1. スタブ登録 します。
スタブとは

スタブ(Stub)は、ある処理を代替することをいい、テスト中に特定の入力に対して期待される出力を返します。スタブは主に外部システムや時間がかかる処理(例えば、APIリクエストやネットワーク経由でのデータベースアクセス)の呼び出しを避けるために使用されます。スタブはその振る舞いがあらかじめ定義されており、テストの際にはそれ以上の機能や状態の検証は行いません。

RSpec(例)
require 'rails_helper'
require 'webmock/rspec'

RSpec.describe "画像生成", type: :system do
  before do
    stub_request(:post, "https://APIのエンドポイントURL")
    .to_return(status: 200, body: "ダミーレスポンス", headers: {})
  end
〜省略〜

stub_request によってスタブ登録(模倣データの準備)をしておくことで、 https://APIのエンドポイントURL にリクエストしようとした際に処理をインターセプト(捕捉)して模倣データを返してくれます。

さいごに

WebMock により外部APIのレスポンスの依存性を減らすことでテストの安定性が高まり、APIへの負荷も低減させることができ、延いては従量課金型APIのコストを抑制することもできます。
外部API呼び出しのあるRailsアプリケーションを開発している方の参考になると嬉しいです。

Discussion