Declaring mocks
Mocks are declared in your repository, next to the package that owns the integration. The declaration has two parts: mocks.yaml, which lists the services a package talks to, and optional rules files, which describe the responses.
Discovering what to declare
mocks.yaml is generated. The scanner walks your project — Named Credentials, External Service Registrations and Apex callout patterns — and writes a declaration per package:
sfp project integrations scanThe scan also compares against a target org when one is supplied (-o <alias>), so integrations that exist only in the org (a common situation for credentials managed outside version control) are discovered too. Re-running the scan preserves any edits you have made — it only adds newly discovered services.
Add --scaffold-rules to also generate starter rules files: when a service has an OpenAPI spec, the scaffold contains a happy-path rule per operation plus one rule per declared error response; without a spec, the scaffold is inferred from the paths your Apex actually calls.
mocks.yaml
Lives at mocks/mocks.yaml inside the package directory:
apiVersion: sfp.flxbl.io/mocks/v1
services:
- name: OrderStatusApi # logical service name
namedCredential: Order_Status_Service # the Named Credential that gets switched
externalService: OrderStatusApi # optional — External Service for spec-based mocking
rules: ./OrderStatusApi.rules.yaml # optional — custom responsesA service is virtualizable when it is declared here and its Named Credential exists in the review environment. If an externalService is declared, its OpenAPI spec is used to generate deterministic stubs for every operation; the rules file then only needs to cover the paths where you want specific behaviour. Services without a spec are served from their rules plus a catch-all, so an unmocked path returns a generic success instead of hanging the org.
Rules files
A rules file describes responses for specific requests:
apiVersion: sfp.flxbl.io/mocks/v1
rules:
- request:
method: GET
path: /orders/{orderId}/status # {param} matches one path segment
response:
status: 200
body:
status: PROCESSING
eta: '2026-06-20'
- request:
method: POST
path: /payments
bodyContains: '"currency":"JPY"' # substring match on the request body
response:
status: 422
body: { error: unsupported currency }
- request:
method: GET
path: /flags/health
scenario: flaky-then-ok # ordered sequence instead of a single response
responses:
- { status: 503, body: { error: warming up } }
- { status: 200, body: { ok: true } } # the last response repeatsThe pieces:
- Request matching —
methodandpathare required. Paths support{param}and*placeholders (each matches a single path segment), exactqueryparameters, andbodyContainssubstring matching. When several rules match, the one declared earlier in the file wins — order them from specific to general. - Responses —
statusplus an optionalbody(an object is served as JSON, a string verbatim),headers, anddelayMsto inject latency. - Sequences — a rule with
scenarioandresponsesserves its responses in order on repeated calls, and the last one repeats. This is how you model fail-then-recover behaviour for retry testing.
Rules always take precedence over spec-generated stubs, which take precedence over the catch-all. In practice that means a rules file overrides individual paths while the spec keeps covering the rest of the API.
Where responses are deliberately simple
Responses are static by design — there is no templating, no echoing of request data and no generated identifiers. Deterministic responses are what make review feedback reproducible. When a test case needs responses that change across repeated calls, use scenario playbooks.
Trying mocks locally
Everything above also works without a server or a pull request, against any WireMock instance you run yourself:
sfp project integrations virtualize --workspace dev --wiremock-url http://localhost:8080
sfp project integrations virtualize --workspace dev --wiremock-url http://localhost:8080 --watch--watch reloads the mocks whenever you save a rules file or playbook, so a rule can be verified with curl immediately after editing it. A broken yaml file reports the error and keeps the previous mocks serving.