はじめに
本記事では、Elixir 開発者の「HTTP クライアントのテストをもっと簡単にしたい」という悩みに応える ExVCR を紹介します。
ExVCR は、テスト時に実際の HTTP リクエストを録画・再生し、ネットワークアクセスなしで再現性のあるテストを可能にする強力なツールです。
以下のような場面で役立ちます。
- 外部 API の応答を再現したい
- ネットワーク障害時にもテストを実行したい
- 予期せぬ API の変更を検出したい
ExVCR の基本
ExVCRは、Ruby の VCR にインスパイアされた Elixir 用 HTTP モックライブラリです。
HTTP リクエストとそのレスポンスを録画 (record)・再生 (replay) することで、再現性のあるテストを簡単に実現します。
主な特徴は以下のとおりです。
- カセットベースのアプローチ: リクエストとレスポンスを「カセット」として保存し、必要に応じて再生
- 多様な HTTP ライブラリをサポート: Finch、HTTPoison、:httpc など複数のライブラリに対応
- 設定可能なフィルタリング: 秘密情報(例: API キー)をマスキング可能
- 簡単なセットアップとカスタマイズ: デフォルトの設定でも即使用可能
同様の状況での代替アプローチ
- 手動でモック: 手動でモックを作成して HTTP クライアントの挙動を模倣する方法です。モックを使用すると、外部 API への依存を取り除くことができますが、リクエストやレスポンスの詳細を正確に再現するのが難しい場合があります。
- Bypass を利用: Bypass を使うと、テスト用にローカルサーバーを立ち上げ、リクエストをキャプチャして処理を模倣できます。これにより、テストをよりリアルに近づけることができますが、セットアップが煩雑になりがちです。
- リアル API との統合テスト: 実際のエンドポイントを利用してテストすることで、外部 API の挙動をそのまま確認できます。ただし、この方法はネットワーク状態に依存し、API が不安定な場合や変更があった場合にテストが失敗するリスクがあります。
これらの方法はそれぞれ一長一短がありますが、管理や保守の手間を考慮すると、ExVCRが非常に便利です。
ExVCRを利用すれば、リクエストとレスポンスを録画して保存しておくだけで、簡単にテストを再現できます。
HTTP リクエストとレスポンスは JSON 形式で保存されます。デフォルトではvcr_cassettes
ディレクトリに自動保存されます。一方、手動で管理したい場合はcustom_cassettes
ディレクトリを利用することができます。vcr_cassette_library_dir
およびcustom_cassette_library_dir
の設定項目を使って保存先を変更することも可能です。
踏み出せば、その一足が道となる。
まずはExVCRのセットアップに挑戦してみましょう!
インストールとセットアップ
インストール
以下を mix.exs
の deps
に追加します。
defp deps do
[
{:exvcr, "~> 0.15.2", only: :test},
{:req, "~> 0.5.6"} # HTTPクライアント用
]
end
次に、依存関係を取得します。
mix deps.get
初期設定
ExVCRはデフォルト設定のままで十分に動作します。初めて使用する場合は追加設定を行わず、そのまま動作確認を進めて問題ありません。
特定の要件でデフォルトの動作を上書きしたい場合のみ、config/config.exs
に手動設定が必要です。
例えば、デフォルトでは、カセットはプロジェクトのルートディレクトリ直下に保存されますが、保存先を明示的にtest/fixture/vcr_cassettes
に変更する場合には以下のように設定します。
import Config
config :exvcr,
vcr_cassette_library_dir: "test/fixture/vcr_cassettes"
詳細は、公式ドキュメントをご参照ください。
シンプルな例
例として、https://jsonplaceholder.typicode.com のダミー API からデータを取得するテストを作成します。
API クライアントモジュール
defmodule HelloExvcr.ApiClient do
@api_base_url "https://jsonplaceholder.typicode.com"
def get_todo(id) do
Req.get!("#{@api_base_url}/todos/#{id}")
end
end
テストモジュール
defmodule HelloExvcr.ApiClientTest do
use ExUnit.Case
use ExVCR.Mock, adapter: ExVCR.Adapter.Finch
setup do
ExVCR.Config.cassette_library_dir("test/fixture/vcr_cassettes")
:ok
end
test "get_todo" do
use_cassette "get_todo_example" do
{:ok, response} = HelloExvcr.ApiClient.get_todo(1)
assert response.body["title"] == "delectus aut autem"
end
end
end
最初のテスト実行でカセットが作成され、その後はカセットを使ったテストが実行されます。
応用例
カスタムマッチャー
ExVCRでは、デフォルトのリクエスト比較条件に加えて、カスタムマッチャーを使用して特定のニーズに応じた比較ロジックを設定できます。たとえば、クエリパラメータや特定のヘッダーに基づいてリクエストを区別する場合に便利です。これにより、より柔軟なテストケースの記述が可能になります。
defmodule HelloExvcr.ApiClient do
@api_base_url "https://jsonplaceholder.typicode.com"
def get_todo_with_params(id, params) do
url = "#{@api_base_url}/todos/#{id}"
Req.get!(url, params: params)
end
end
defmodule HelloExvcr.ApiClientTest do
use ExUnit.Case
use ExVCR.Mock, adapter: ExVCR.Adapter.Finch
test "get_todo_with_custom_matcher" do
use_cassette "custom_matcher_example", match_requests_on: [:query, :headers] do
response =
HelloExvcr.ApiClient.get_todo_with_params(1, %{
"filter" => "completed",
"userId" => 123
})
assert response.status == 200
assert response.body["title"] == "delectus aut autem"
end
end
end
秘密情報の除外
API キーやアクセストークンなどの秘密情報をテストカセットに記録しないようにするために、フィルタリング機能を利用できます。この機能では、指定したパターンをプレースホルダーに置き換えることで、セキュリティを確保しながらカセットを共有できます。
以下の設定例では、API_TOKEN
という環境変数の値を<API_TOKEN>
に置き換えます。
config :exvcr,
filter_sensitive_data: [
[pattern: System.fetch_env!("API_TOKEN"), placeholder: "<API_TOKEN>"]
]
リクエストボディやヘッダーの比較
ExVCRでは、リクエストボディやヘッダーの内容を細かく比較する設定が可能です。これにより、同じエンドポイントに対して異なるパラメータやヘッダーで行われたリクエストを区別できます。この機能は、複雑な API のテストで非常に役立ちます。
具体例として、以下のようにカスタムロジックを組み込むことで、比較条件を詳細に定義できます。
use_cassette "example", match_requests_on: [:query, :headers] do
response = HelloExvcr.ApiClient.get_todo_with_params(1, %{filter: "completed"})
assert response.status == 200
end
カスタムカセット
ExVCR では、用途に応じて複数のカセット保存先を設定し、個別に管理することができます。
例えば、テストケースごとに異なるディレクトリにカセットを保存する場合、以下のようにカスタムカセットディレクトリを指定できます。
config :exvcr,
custom_cassette_library_dir: "test/fixture/custom_cassettes"
テストモジュール内では以下のように使用します。
use_cassette "custom_cassettes/api_client/test_specific_case" do
response = HelloExvcr.ApiClient.get_todo(123)
assert response.status == 200
end
この設定では、test/fixture/custom_cassettes/api_client/test_specific_case.json
にカセットが保存されます。
おわりに
ExVCRを使うことで、HTTP テストが驚くほど簡単になります。本記事を参考に、テストの効率化に挑戦してみてください。
この記事のコードは GitHub リポジトリで公開しています。以下をご覧ください:
何か氣づいた点があれば、コメントで共有していただけると嬉しいです。