# `Req.Test` stubs keep HTTP out of your tests

`Req` ships with a built-in test stub mechanism. No Bypass, no Mox, no additional dependencies — just three small changes and your HTTP calls never hit the network in tests.

```elixir
# 1. lib/my_app/weather_client.ex — read options from config
defmodule MyApp.WeatherClient do
  def get_forecast(city) do
    req_options = Application.get_env(:my_app, :weather_req_options, [])

    Req.new(base_url: "https://api.weather.com")
    |> Req.merge(req_options)
    |> Req.get!(url: "/forecast", params: [city: city])
  end
end

# 2. config/test.exs — point the plug at Req.Test
config :my_app, :weather_req_options,
  plug: {Req.Test, MyApp.WeatherClient},
  retry: false

# 3. test — stub and assert
test "get_forecast/1 returns the forecast for a city" do
  Req.Test.stub(MyApp.WeatherClient, fn conn ->
    Req.Test.json(conn, %{"forecast" => "sunny", "city" => "London"})
  end)

  response = MyApp.WeatherClient.get_forecast("London")

  assert response.body["forecast"] == "sunny"
end
```

The stub receives a `Plug.Conn` — the same conn you know from Phoenix. `Req.Test.json/2` is a convenience that sets the content-type header and JSON-encodes the response body for you.

The key insight is that `plug: {Req.Test, MyApp.WeatherClient}` acts as a transport-level interceptor. Instead of opening a TCP connection, `Req` calls your plug function directly. The test process owns the stub, so concurrent tests with `async: true` are safe without any extra setup.

You can inspect the outgoing request inside the stub too — useful when you want to assert on what your code is actually sending:

```elixir
Req.Test.stub(MyApp.WeatherClient, fn conn ->
  assert conn.params["city"] == "London"
  Req.Test.json(conn, %{"forecast" => "sunny"})
end)
```

And `Req.Test.transport_error/2` lets you simulate network failures to test your error handling:

```elixir
Req.Test.stub(MyApp.WeatherClient, fn conn ->
  Req.Test.transport_error(conn, :timeout)
end)
```

If your Req call happens inside a spawned `Task`, the stub won't be visible to the child process by default. Pass ownership explicitly with `Req.Test.allow/3` — or see `Task.Supervisor` sandbox allowance patterns for the full setup.

[`Req.Test` docs](https://hexdocs.pm/req/Req.Test.html)


---

Created by: almirsarajcic
Date: May 13, 2026
URL: https://elixirdrops.net/d/jTokrLpB
