Mock API Server
Lab can serve OpenAPI-described mock APIs over HTTP. Use mock APIs when a test needs stable REST responses without depending on an external service.
During lab run, mock service URLs are passed to FQL under @lab.mock.
Serve a mock API during a test run
Use run --mock to serve an OpenAPI-compatible mock API while tests run.
lab run --mock ./users.yaml@api tests/
The service URL is available as @lab.mock.api.
Multiple mock APIs can be served in the same run:
lab run \ --mock ./users.yaml@users \ --mock ./billing.yaml@billing \ tests/
Start standalone mock APIs
Use lab serve --mock when you want a mock API service without running tests.
lab serve --mock ./users.yaml@api
Standalone serve entries must be explicit. Positional service entries are rejected; use --mock for mock API specs.
Lab prints the URL for each started service and runs until the process is cancelled.
Entry syntax
Mock entries use this binding syntax:
| Syntax | Meaning |
|---|---|
<path> |
Serve the spec on a dynamic port with the filename, without extension, as the alias. |
<path>:<port> |
Serve the spec on a fixed port with the filename, without extension, as the alias. |
<path>@<alias> |
Serve the spec with an explicit alias. |
<path>@<alias>:<port> |
Serve the spec with an explicit alias and fixed port. |
Mock entries must point to existing files. The default alias is the spec filename without its extension.
Aliases must start with a letter or underscore and may contain letters, numbers, underscores, and hyphens. Use bracket access for aliases that are not valid FQL dotted-property names:
Duplicate aliases are rejected.
Fixed ports
Use a fixed port when another process needs a stable URL.
lab serve --mock ./users.yaml@api:8081
The fixed port is part of the service binding. The alias is still api, so test scripts use @lab.mock.api.
Mock API specs
Mock specs use OpenAPI paths with operation-level x-lab-mock blocks.
openapi: 3.1.0
info:
title: Users API
version: 1.0.0
paths:
/users/{id}:
get:
x-lab-mock:
status: 200
headers:
X-Test-Server: lab
body:
id: "{{ .Path.id }}"
name: "User {{ .Path.id }}"
The mock server currently handles GET, POST, PUT, PATCH, and DELETE operations. Paths must start with /.
x-lab-mock supports:
| Field | Meaning |
|---|---|
status |
HTTP status code. Defaults to 200. |
headers |
Response headers as string values. |
body |
Structured JSON response body. String values are rendered as templates. |
bodyTemplate |
Raw text response template. Mutually exclusive with body. |
When body is used, Lab writes JSON and defaults Content-Type to application/json unless the mock sets it. When bodyTemplate is used, Lab writes text and defaults Content-Type to text/plain; charset=utf-8.
Template context
Mock response templates use Go template syntax. They receive this context:
| Field | Meaning |
|---|---|
.Method |
Request method. |
.Path |
Path parameters from routes such as /users/{id}. |
.Query |
Query parameters. |
.Headers |
Request headers. |
.Body |
Parsed JSON request body, or nil. |
Example:
paths:
/echo/{name}:
post:
x-lab-mock:
status: 201
body:
method: "{{ .Method }}"
name: "{{ .Path.name }}"
active: "{{ .Body.active }}"
Request bodies used by templates must be JSON. If the request body is not valid JSON, the mock server returns 400 Bad Request.
Bind and advertised hosts
By default, mock services bind to 127.0.0.1 and advertise URLs with 127.0.0.1.
Use --serve-bind to choose the listener host. Use --serve-host to choose the host placed in generated URLs.
lab run \ --mock ./users.yaml@api \ --serve-bind 0.0.0.0 \ --serve-host host.docker.internal \ tests/
Both values are hosts only. Do not include a port.
When --serve-host is set and --serve-bind is omitted, Lab binds to a wildcard host so remote runtimes can reach the service: 0.0.0.0 for IPv4 and hostnames, or :: for IPv6 literals.