mock
stableCreate in-memory HTTP mock servers for testing, with route matching, call count tracking, expectation verification, and recorded argument inspection.
use plugin mock::{create_request, create_response, MockServer.new, …} Functions (14)
- create_request Build a request table with method, url, and body
- create_response Build a response table with status and body
- MockServer.new Create a new empty mock server
- add_route Register a route with a canned status, body, and headers
- match_request Find a matching route, return its response, increment its counter
- routes List all registered routes with their call counts
- clear Remove all routes from the server
- remove_route Remove a single route by method and path
- call_count Get how many times a route has been matched
- expect Set the expected call count for a route
- verify Return the list of unmet expectations (empty = all passed)
- reset_counts Reset all call counters without removing routes
- recorded_args Get the recorded arguments for every call to a route
- route_count Get the total number of registered routes
Overview
mock lets you stand up a lightweight, in-memory HTTP server entirely inside your
test code — no sockets, no ports, no real network. The core concept is the
MockServer class: a stateful handle that holds a list of routes, where each
route maps a method and path to a canned status, body, and headers. As your code
under test "sends" requests through match_request, the server records how many
times each route was hit and what arguments came with each call.
The mental model is: create a server with MockServer.new, register the
responses you expect with add_route, drive your code so it calls
match_request, then assert on behaviour with call_count, recorded_args, or
the expect/verify pair. Two free helpers, create_request and
create_response, build plain request/response tables when you just need a
data shape rather than a full server. Reach for mock whenever you are
unit-testing code that talks to an HTTP API and want deterministic, inspectable
responses.
Common patterns
Register routes, serve a request, and assert on the response:
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/users", 200, '{"users":[]}', #{})
server.add_route("POST", "/users", 201, '{"id":1}', #{})
let resp = server.match_request("GET", "/users")
print("status: {resp["status"]}")
print("body: {resp["body"]}")
let missing = server.match_request("DELETE", "/users")
print(missing)
Set an expectation up front, exercise the code, then verify it was met:
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/health", 200, "ok", #{})
server.expect("GET", "/health", 2)
server.match_request("GET", "/health")
server.match_request("GET", "/health")
let failures = server.verify()
print("called {server.call_count("GET", "/health")} times")
print("expectation failures: {failures}")
Inspect what each call carried, then reset counters to reuse the server:
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("POST", "/login", 200, '{"token":"abc"}', #{})
server.match_request("POST", "/login", #{ "user": "ada" })
let calls = server.recorded_args("POST", "/login")
print("recorded calls: {calls}")
server.reset_counts()
print("after reset: {server.call_count("POST", "/login")}")
Build a request table with method, url, and body
Builds a plain table representing an HTTP request with method, url, and
body fields. Useful for constructing test inputs by hand. The body argument
is optional and defaults to an empty string when omitted.
use plugin mock::{create_request}
let req = create_request("GET", "/api/users", "")
print(req["method"])
print(req["url"])
It pairs with a server when you want to pass a prebuilt request shape around your own test helpers:
use plugin mock::{create_request, MockServer}
let server = MockServer.new()
server.add_route("POST", "/api/users", 201, '{"id":7}', #{})
let req = create_request("POST", "/api/users", '{"name":"Ada"}')
let resp = server.match_request(req["method"], "/api/users")
print(resp["status"])
Build a response table with status and body
Builds a plain table representing an HTTP response with status and body
fields. Handy for asserting against expected response shapes.
use plugin mock::{create_response}
let resp = create_response(200, '{"ok": true}')
print(resp["status"])
print(resp["body"])
Create a new empty mock server
Creates a new, empty mock server with no routes registered. Returns a
MockServer handle whose methods are called with dot syntax.
use plugin mock::{MockServer}
let server = MockServer.new()
print("routes so far: {server.route_count()}")
Register a route with a canned status, body, and headers
Registers a route on the server. When a later match_request matches the same
method (case-insensitive) and path, this route's status, body, and headers
are returned. Pass headers as a map literal of string keys to string values
(use #{} for none).
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/users", 200, '{"users":[]}', #{})
server.add_route("POST", "/users", 201, '{"id":1}', #{ "Location": "/users/1" })
print("registered {server.route_count()} routes")
Find a matching route, return its response, increment its counter
Looks up a route by method and path. On a match it increments that route's call
counter and returns a table with status, body, and headers. Returns nil
when no route matches. An optional third map argument is also accepted: its
string key/value pairs are stored as the recorded arguments for the call.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/ping", 200, "pong", #{})
let resp = server.match_request("GET", "/ping")
print(resp["body"])
let none = server.match_request("GET", "/missing")
print(none)
Capture per-call arguments by passing a map as the third argument:
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("POST", "/search", 200, "[]", #{})
server.match_request("POST", "/search", #{ "q": "zolo" })
print(server.recorded_args("POST", "/search"))
List all registered routes with their call counts
Returns a table of all registered routes. Each entry exposes method, path,
status, body, and call_count, making it a quick way to snapshot the
server's state.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/a", 200, "A", #{})
server.add_route("GET", "/b", 200, "B", #{})
server.match_request("GET", "/a")
let all = server.routes()
print("first route: {all[1]["path"]} called {all[1]["call_count"]}x")
Remove all routes from the server
Removes every route from the server, returning it to its freshly-created state.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/x", 200, "x", #{})
server.clear()
print("routes after clear: {server.route_count()}")
Remove a single route by method and path
Removes a single route matching the given method and path, leaving all other routes intact.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/keep", 200, "keep", #{})
server.add_route("GET", "/drop", 200, "drop", #{})
server.remove_route("GET", "/drop")
print("routes left: {server.route_count()}")
Get how many times a route has been matched
Returns how many times the given route has been matched by match_request.
Returns 0 if the route does not exist or was never called.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/hit", 200, "ok", #{})
server.match_request("GET", "/hit")
server.match_request("GET", "/hit")
print("hits: {server.call_count("GET", "/hit")}")
Set the expected call count for a route
Sets the expected number of calls for an already-registered route, to be checked
later with verify. Returns true on success. The route must exist or the call
fails with an error.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/once", 200, "ok", #{})
server.expect("GET", "/once", 1)
server.match_request("GET", "/once")
print("expectations met: {server.verify()}")
Return the list of unmet expectations (empty = all passed)
Checks every route that has an expectation set via expect and returns a table
of failures. Each failure entry contains method, path, expected, and
actual. An empty table means all expectations passed.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/twice", 200, "ok", #{})
server.expect("GET", "/twice", 2)
server.match_request("GET", "/twice")
let failures = server.verify()
print("failures: {failures}")
Reset all call counters without removing routes
Resets every route's call counter and clears its recorded arguments, without removing any routes. Useful for reusing one server across multiple test cases.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/r", 200, "ok", #{})
server.match_request("GET", "/r")
server.reset_counts()
print("after reset: {server.call_count("GET", "/r")}")
Get the recorded arguments for every call to a route
Returns a table of the arguments recorded for each call to the route, in call
order. Each entry is the map that was passed as the third argument to
match_request. Returns nil if the route does not exist.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("POST", "/log", 200, "ok", #{})
server.match_request("POST", "/log", #{ "level": "info" })
server.match_request("POST", "/log", #{ "level": "warn" })
print(server.recorded_args("POST", "/log"))
Get the total number of registered routes
Returns the total number of routes currently registered on the server.
use plugin mock::{MockServer}
let server = MockServer.new()
server.add_route("GET", "/a", 200, "A", #{})
server.add_route("GET", "/b", 200, "B", #{})
print("total routes: {server.route_count()}")