【WebMock+RSpec】テストコードで外部APIを模倣する【余分なAPIコストを抑制】
はじめに
個人開発中のアプリケーションのテストコードを実装する際、テストを行うたびに実際の外部API(画像生成API)を叩くことになるため主に以下の2点の課題を抱えていました。
- レート制限(429エラー)に達してしまいレスポンスが安定しない
- 従量課金型のAPIを導入した場合、テストの度に課金が発生する
これらの課題を解消するにはAPIのレスポンスを模倣(エミュレート)し予め用意した擬似データをAPIレスポンスとして返却する処理が必要です。
これを実現するのが WebMock
というGemです。
テストの安定性を向上させることができ、延いては従量課金APIの場合はテストの度に課金されてしまうことも抑制できます。(コスト削減!)
私の開発ケースで、実装までの工程を紹介したいと思います。
概要
まず、WebMockの概要ですが、 stub_request
によって指定した外部へのHTTPリクエスト(今回のケースだと APIエンドポイントURL
)を捕捉してあらかじめ設定したモック(偽レスポンス)を返すために使用されます。
つまり、 指定したURLと一致するURLに外部アクセスを試みた瞬間にWebMockくんが横入りしてきてあらかじめ用意した模倣データを返してくれる という流れです。
公式ドキュメントは以下
実装手順
-
webmock
をインストールします。
※group :test do〜end
のブロックに記述。
group :test do
# 〜省略〜
gem 'webmock'
end
bundle install
-
WebMockの初期設定
をします。
WebMockはデフォルト設定だとスタブ登録(模倣登録)していないAPIのアクセスを許可しません。←これがややこしい
つまり、模倣しなくてもよい外部リクエストがあった場合でもスタブ登録(模倣登録)しないとアクセスがブロックされ、エラーが返りテストをパスできません。(←わかりづらかったらすみません。要はテスト中に存在する外部リクエストを全部スタブ登録してモック(模倣データを準備)するか、下記のコードを追加してスタブ登録してないAPIのアクセスをブロックされないようにするか
です。)
私のケースでは自作した無料APIへのリクエストは模倣する必要がない(通常どおりAPIリクエストを行っても課金の心配もない)ためスタブ化していないAPIへのアクセスを許可する記述
を追加します。
require 'webmock'
WebMock.allow_net_connect!(:net_http_connect_on_start => true)
-
スタブ登録
します。
スタブとは
スタブ(Stub)は、ある処理を代替することをいい、テスト中に特定の入力に対して期待される出力を返します。スタブは主に外部システムや時間がかかる処理(例えば、APIリクエストやネットワーク経由でのデータベースアクセス)の呼び出しを避けるために使用されます。スタブはその振る舞いがあらかじめ定義されており、テストの際にはそれ以上の機能や状態の検証は行いません。
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