Current section

Files

Jump to
req_cassette lib req_cassette.ex
Raw

lib/req_cassette.ex

defmodule ReqCassette do
@moduledoc """
A VCR-style record-and-replay library for Elixir's [Req](https://hexdocs.pm/req) HTTP client.
ReqCassette captures HTTP responses to files ("cassettes") and replays them in subsequent
test runs, making your tests faster, deterministic, and free from network dependencies.
## Features
- 🎬 **Record & Replay** - Capture real HTTP responses and replay them instantly
- ⚑ **Async-Safe** - Works with `async: true` in ExUnit (unlike ExVCR)
- πŸ”Œ **Built on Req.Test** - Uses Req's native testing infrastructure (no global mocking)
- πŸ€– **ReqLLM Integration** - Perfect for testing LLM applications
- πŸ“ **Human-Readable** - JSON cassettes you can inspect and edit
- 🎯 **Simple API** - Just add `plug: {ReqCassette.Plug, ...}` to your Req calls
## Quick Start
# First call - records to cassette
response = Req.get!(
"https://api.example.com/users/1",
plug: {ReqCassette.Plug, %{cassette_dir: "test/cassettes"}}
)
# Second call - replays from cassette (instant, no network!)
response = Req.get!(
"https://api.example.com/users/1",
plug: {ReqCassette.Plug, %{cassette_dir: "test/cassettes"}}
)
## Installation
Add to your `mix.exs`:
def deps do
[
{:req, "~> 0.5.15"},
{:req_cassette, "~> 0.1.0"}
]
end
## Usage with ReqLLM
{:ok, response} = ReqLLM.generate_text(
"anthropic:claude-sonnet-4-20250514",
"Explain recursion",
max_tokens: 100,
req_http_options: [
plug: {ReqCassette.Plug, %{cassette_dir: "test/cassettes"}}
]
)
First call costs money, subsequent calls are FREE - replayed from cassette!
## How It Works
1. **Record Mode**: First request β†’ Hits real API β†’ Saves response to JSON file
2. **Replay Mode**: Subsequent requests β†’ Loads from JSON β†’ Returns instantly
The cassette is matched by HTTP method, path, query string, and request body.
## ExUnit Example
defmodule MyApp.APITest do
use ExUnit.Case, async: true
@cassette_dir "test/fixtures/cassettes"
setup do
File.mkdir_p!(@cassette_dir)
:ok
end
test "fetches user data" do
response = Req.get!(
"https://api.example.com/users/1",
plug: {ReqCassette.Plug, %{cassette_dir: @cassette_dir}}
)
assert response.status == 200
assert response.body["name"] == "Alice"
end
end
## Documentation
See `ReqCassette.Plug` for detailed configuration options and API reference.
## Why ReqCassette over ExVCR?
| Feature | ReqCassette | ExVCR |
| -------------- | --------------- | -------------------- |
| Async-safe | βœ… Yes | ❌ No |
| HTTP client | Req only | hackney, finch, etc. |
| Implementation | Req.Test + Plug | :meck (global) |
| Maintenance | Low | High |
ReqCassette uses Req's native plug system instead of global mocking, making it
process-isolated, async-safe, and compatible with any Req adapter.
"""
end