Guides
Testing integrations locally
Your application code, test runner, and editor stay exactly where they are. The only thing that moves is where its integrations point: instead of production APIs (side effects) or hand-kept mocks (drift), the app talks to hosted twins — stateful, deterministic replicas with public per-service endpoints. Nothing to install or run locally for the twins themselves.
How it works
- Runs on your machine: your app, its tests, and an env file selecting the twin endpoints.
- Runs hosted: the twins (seeded state, REST + MCP + CLI), their console, and their state inspection.
- The seam: one base URL per service. Every mounted operation lives under
/api/v1/sandbox/worlds/<world_id>/services/<service>/operations/<operation_id>, addressed by the vendor’s own operation ids.
Spin up the twins
$ blobfish twin-runs create --twins slack,stripe --ttl 60 --wait
# writes routing.env into the current directory:
# BLOBFISH_TWIN_ENDPOINT=… (world MCP)
# BLOBFISH_TWIN_SLACK_MCP=… (per-service MCP)
# BLOBFISH_TWIN_STRIPE_MCP=…Load routing.envin your test setup (or export the variables) and derive each service’s REST base from its MCP line by dropping the trailing /mcp.
Example: a Slack messaging workflow
# list seeded channels
$ curl -sS "$SLACK_TWIN_BASE/operations/conversations.list?token=xoxb-test" \
-H "X-API-Key: $BLOBFISH_API_KEY"
# post — the twin validates required params exactly like the vendor
$ curl -sS -X POST $SLACK_TWIN_BASE/operations/chat.postMessage \
-H "X-API-Key: $BLOBFISH_API_KEY" -H "Content-Type: application/json" \
-d '{"token":"xoxb-test","channel":"C0000001","text":"deploy finished"}'
# assert on state, not just responses
$ curl -sS $SLACK_TWIN_BASE/state -H "X-API-Key: $BLOBFISH_API_KEY"The write response carries the row-level diff the call produced; the /state endpoint returns the tables so your test can assert on what actually changed, not only on the HTTP body.
Isolation and repeatability
- One session per test run:
POST …/sessions(or./bf-twin session new) and sendx-blobfish-sessionon every call — parallel CI shards each get an isolated copy-on-write fork of the seeded state. - Reset between cases:
POST …/services/<service>/resetrestores the seed baseline for that fork in one call. - Determinism: the seed is stable per twin, so the same test sees the same starting rows on every run.
Managing the environment
$ blobfish twin-runs list
$ blobfish twin-runs extend <env_id> --ttl 60
$ blobfish twin-runs delete <env_id> # delists; the world's data persistsRestoring your original environment is deleting routing.env— your app’s real configuration was never touched.
Tips
- Discover operation ids with
./bf-twin <service> ops— the listing is generated from the same specs the routes serve. - Writes sent as GET are rejected with 405 — codify the split in your HTTP helper once.
- The environment console page shows the live topology graph and per-service state while your tests run — see the quickstart.