A 100% Elixir, 0-dependency mock HTTP server for testing HTTP clients.
Passby provides a clean, drop-in replacement for Bypass without requiring
Plug, Cowboy, or Ranch.
Basic Usage
test "fetches user profile" do
bypass = Passby.open()
Passby.expect_once(bypass, "GET", "/api/users/42", fn conn ->
Passby.Conn.put_resp_header(conn, "content-type", "application/json")
Passby.Conn.resp(conn, 200, ~s({"id": 42, "name": "Alice"}))
end)
assert {:ok, %{"name" => "Alice"}} = MyClient.get_user("#{bypass.url}/api/users/42")
endPlug and Bypass Compatibility
Passby.Conn implements the same attributes and helpers as Plug.Conn
(get_req_header/2, put_resp_header/3, resp/3, send_resp/1), allowing
existing test suites to migrate from Bypass with zero friction.
Summary
Functions
Closes the listening socket to simulate network downtime or connection refused errors.
Adds an expectation that will be called for any incoming request.
Adds an expectation for a specific HTTP method and path.
Adds an expectation that will be called at most once for any request.
Adds an expectation that will be called at most once for a specific method and path.
Gets request headers from the connection. Delegate to Passby.Conn.get_req_header/2.
Starts a new Passby mock HTTP server instance.
Clears all expectations and stubs configured on the Passby instance.
Sets a response header. Delegate to Passby.Conn.put_resp_header/3.
Sets the response status and body. Delegate to Passby.Conn.resp/3.
Sends the configured response. Delegate to Passby.Conn.send_resp/1.
Sends the response immediately. Delegate to Passby.Conn.send_resp/3.
Adds a fallback stub handler that matches if no specific expectation matched.
Reopens the listening socket after being closed with down/1.
Returns the base URL of the Passby instance.
Returns the URL of the Passby instance with the given path appended.
Types
@type t() :: %Passby{pid: pid(), port: :inet.port_number(), url: String.t()}
Functions
@spec down(t()) :: :ok
Closes the listening socket to simulate network downtime or connection refused errors.
@spec expect(t(), (Passby.Conn.t() -> any())) :: :ok
Adds an expectation that will be called for any incoming request.
Examples
Passby.expect(bypass, fn conn ->
Passby.Conn.resp(conn, 200, "OK")
end)
Adds an expectation for a specific HTTP method and path.
Examples
Passby.expect(bypass, "POST", "/api/v1/checkout", fn conn ->
Passby.Conn.resp(conn, 201, "Created")
end)
@spec expect_once(t(), (Passby.Conn.t() -> any())) :: :ok
Adds an expectation that will be called at most once for any request.
@spec expect_once( t(), String.t() | atom() | nil, String.t() | nil, (Passby.Conn.t() -> any()) ) :: :ok
Adds an expectation that will be called at most once for a specific method and path.
Examples
Passby.expect_once(bypass, "DELETE", "/sessions/current", fn conn ->
Passby.Conn.resp(conn, 204, "")
end)
Gets request headers from the connection. Delegate to Passby.Conn.get_req_header/2.
Starts a new Passby mock HTTP server instance.
Options
:port- The port number to listen on (default0for dynamic ephemeral port).:bind_address- The IP address tuple to bind to (default{127, 0, 0, 1}).
Examples
iex> bypass = Passby.open()
iex> is_integer(bypass.port)
true
iex> is_binary(bypass.url)
true
iex> bypass.url =~ "http://127.0.0.1:"
true
@spec pass(t()) :: :ok
Clears all expectations and stubs configured on the Passby instance.
Sets a response header. Delegate to Passby.Conn.put_resp_header/3.
Sets the response status and body. Delegate to Passby.Conn.resp/3.
Sends the configured response. Delegate to Passby.Conn.send_resp/1.
Sends the response immediately. Delegate to Passby.Conn.send_resp/3.
Adds a fallback stub handler that matches if no specific expectation matched.
@spec up(t()) :: :ok
Reopens the listening socket after being closed with down/1.
Returns the base URL of the Passby instance.
Examples
iex> bypass = Passby.open()
iex> Passby.url(bypass) == bypass.url
true
Returns the URL of the Passby instance with the given path appended.
Examples
iex> bypass = Passby.open()
iex> Passby.url(bypass, "/api/users/42") == "#{bypass.url}/api/users/42"
true
iex> Passby.url(bypass, "api/users/42") == "#{bypass.url}/api/users/42"
true