Passby (passby v0.1.1)

Copy Markdown View Source

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")
end

Plug 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 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

t()

@type t() :: %Passby{pid: pid(), port: :inet.port_number(), url: String.t()}

Functions

down(passby)

@spec down(t()) :: :ok

Closes the listening socket to simulate network downtime or connection refused errors.

expect(passby, fun)

@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)

expect(passby, method, path, fun)

@spec expect(t(), String.t() | atom() | nil, String.t() | nil, (Passby.Conn.t() ->
                                                            any())) :: :ok

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)

expect_once(passby, fun)

@spec expect_once(t(), (Passby.Conn.t() -> any())) :: :ok

Adds an expectation that will be called at most once for any request.

expect_once(passby, method, path, fun)

@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)

get_req_header(conn, header_name)

Gets request headers from the connection. Delegate to Passby.Conn.get_req_header/2.

open(opts \\ [])

@spec open(keyword()) :: t()

Starts a new Passby mock HTTP server instance.

Options

  • :port - The port number to listen on (default 0 for 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

pass(passby)

@spec pass(t()) :: :ok

Clears all expectations and stubs configured on the Passby instance.

put_resp_header(conn, key, value)

Sets a response header. Delegate to Passby.Conn.put_resp_header/3.

resp(conn, status, body)

Sets the response status and body. Delegate to Passby.Conn.resp/3.

send_resp(conn)

Sends the configured response. Delegate to Passby.Conn.send_resp/1.

send_resp(conn, status, body)

Sends the response immediately. Delegate to Passby.Conn.send_resp/3.

stub(passby, method, path, fun)

@spec stub(t(), String.t() | atom() | nil, String.t() | nil, (Passby.Conn.t() ->
                                                          any())) :: :ok

Adds a fallback stub handler that matches if no specific expectation matched.

up(passby)

@spec up(t()) :: :ok

Reopens the listening socket after being closed with down/1.

url(passby)

@spec url(t()) :: String.t()

Returns the base URL of the Passby instance.

Examples

iex> bypass = Passby.open()
iex> Passby.url(bypass) == bypass.url
true

url(passby, path)

@spec url(t(), String.t()) :: String.t()

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