lostbean

lostbean

ReqCassette - VCR-style testing for Req with async support

Hello Elixir community! :wave:

I’m excited to announce the first release of ReqCassette, a VCR-style record-and-replay library specifically designed for Req.

What is ReqCassette?

ReqCassette captures HTTP responses to JSON files (“cassettes”) and replays them in subsequent test runs, making your tests:

  • Faster - No network calls after initial recording
  • Deterministic - Same response every time
  • Offline-capable - Work without internet once cassettes are recorded
  • Cost-effective - Perfect for testing paid APIs (like LLM services!)

Why build this

ReqCassette leverages Req’s native testing infrastructure instead of global mocking, making it process-isolated and fully compatible with ExUnit’s async testing.

Quick Example

defmodule MyApp.APITest do
  use ExUnit.Case, async: true

  test "fetches user data" do
    # First run: records to cassette
    # Subsequent runs: instant replay!
    response = Req.get!(
      "https://api.example.com/users/1",
      plug: {ReqCassette.Plug, %{cassette_dir: "test/cassettes"}}
    )

    assert response.status == 200
    assert response.body["name"] == "Alice"
  end
end

Created with ReqLLM in mind

One of my favorite use cases is testing LLM applications 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 and it’s slow, but every subsequent test is fast and
FREE - the response is replayed from the cassette! This makes testing with LLMs
much more practical.

How It Works

ReqCassette uses Req’s :plug option to intercept requests:

  1. First request → Forwards to real server → Saves response to JSON →
    Returns response
  2. Subsequent requests → Loads JSON cassette → Returns saved response

Cassettes are matched by HTTP method, path, query string, and request body,
creating a unique MD5 hash for the filename.

What’s Next?

Some ideas for future versions:

  • Additional recording modes (:once, :none, :all)
  • Custom cassette naming
  • Header-based matching
  • Cassette expiration/TTL
  • Request filtering/sanitization helpers

Feedback Welcome!

This is the first release, and I’d love to hear your thoughts:

  • Are there features you’d like to see?
  • Any issues or bugs to report?

Thanks for reading, and happy testing! :test_tube:

Resources

https://github.com/lostbean/req_cassette


Note: Special thanks to the Req maintainers for building such a fantastic HTTP
client with great testing support, and to the ReqLLM maintainers for creating a
promising and foundational library that makes Elixir shine in the new LLM
space!

Most Liked

lostbean

lostbean

ReqCassette v0.2.0 Update :clapper_board:

Quick update on ReqCassette! v0.2.0 is now available with some nice
improvements:

What’s New:

  • :sparkles: with_cassette/3 function API for cleaner test code
  • :memo: Human-readable cassette filenames (github_user.json instead of
    a1b2c3d4.json)
  • :package: Multiple interactions per cassette file
  • :level_slider: Four recording modes: :replay, :record, :record_missing, :bypass
  • :locked: Sensitive data filtering (headers, regex-based redaction)
  • :bullseye: Configurable request matching
  • :artist_palette: Pretty-printed JSON with native JSON objects (40% smaller)

Example:

import ReqCassette

test "API integration" do
  with_cassette "github_user", [mode: :replay], fn plug ->
    response = Req.get!("https://api.github.com/users/wojtekmach", plug: plug)
    assert response.status == 200
  end
end

First run records, subsequent runs replay instantly (no network, async-safe).

Links:

:warning: Breaking changes from v0.1 - see migration guide.

lostbean

lostbean

ReqCassette v0.3.0 – Safer Recording & Better Filtering

What’s New

:shield: Safer :record mode (breaking change)

  • Fixed dangerous behavior where multi-request tests would lose interactions
  • Simplified API: 4 recording modes → 3
  • Migration: just replace :record_missing with :record

:locked: Better filtering

Quick Example

with_cassette "api_test",
  [filter_request_headers: ["authorization"]],
  fn plug ->
    Req.get!("https://api.example.com", plug: plug)
  end
mikehostetler

mikehostetler

Nice work! Thanks for putting this together!!!

Last Post!

lostbean

lostbean

Happy to hear you found it useful, thanks @Sergio :smiling_face:

Where Next?

Trending in Announcing Top

bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
385 14863 120
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
shahryarjb
The Chelekom project is a library of Phoenix and LiveView components generated via Mix tasks to fit developer needs seamlessly. One of i...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
zachdaniel
Introducing AshStorage! Attachment and file management that slots directly into your resources :smiling_face_with_sunglasses: I had hope...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement