mountebank-imposters
Authors Mountebank imposters (multi-protocol mock servers - HTTP, HTTPS, TCP, SMTP, LDAP, gRPC, WebSockets, GraphQL, and more) by POSTing JSON definitions to the Mountebank control API on port 2525, configures stubs with predicates and responses, and uses record-playback proxy mode to capture upstream traffic. Use when the project needs a multi-protocol mock server beyond HTTP-only tools like WireMock or MSW.
mountebank-imposters
Overview
Mountebank is a multi-protocol service-virtualization tool - "the only open source service virtualization tool that competes with the commercial offerings in terms of protocol diversity, capability, and performance" (mountebank-readme).
Per mountebank-readme, supported protocols include:
Source-fetch note (2026-05-04): the canonical Mountebank documentation domain
mbtest.orgwas hijacked / redirects to an unrelated football-club site (farsleyceltic.com) as of verification on this date. This skill cites the GitHub repository bbyars/mountebank as the authoritative source instead;mbtest.devis the project's current alternate documentation domain per the upstream organization. Verify both URLs before linking from authored content.
When to use
If the team is HTTP-only on the JVM, wiremock-stubs is the lighter fit. For Node / browser HTTP-only, use msw-handlers. Mountebank's strength is multi-protocol breadth; pay the operational cost (a separate process, port 2525) only when you need it.
Install
npm install -g @mbtest/mountebank(Per mountebank-readme.)
For Docker-based CI (preferred for runner cleanliness):
docker run --rm -p 2525:2525 -p 4545:4545 bbyars/mountebank:latest startThe control API listens on port 2525; imposter ports (4545 in the example) are configured per imposter.
Authoring imposters
Mountebank's data model uses these layers:
| Layer | Purpose |
|---|---|
| Imposter | One mock server bound to a port and protocol. |
| Stub | A request matcher attached to an imposter - the response triggered when matched. |
| Predicate | A condition on the incoming request (path, method, header, body, JSON path). |
| Response | The reply Mountebank sends when a stub's predicates match. |
Create an HTTP imposter
POST to the control API:
curl -X POST http://localhost:2525/imposters \
-H 'Content-Type: application/json' \
-d '{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{
"and": [
{ "equals": { "method": "GET", "path": "/orders/42" } }
]
}],
"responses": [{
"is": {
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": "{\"order_id\": 42, \"status\": \"shipped\"}"
}
}]
}]
}'After this POST, GET http://localhost:4545/orders/42 returns the stubbed response.
Predicate operators
Mountebank supports several predicate operators in addition to equals:
| Operator | Meaning |
|---|---|
equals | Exact match. |
deepEquals | Deep equality on a nested object (e.g. JSON body). |
contains | Substring / partial match. |
startsWith / endsWith | Affix matchers. |
matches | Regex match. |
exists | Whether a field is present. |
not | Negate a child predicate. |
or | Boolean OR. |
and | Boolean AND. |
inject | Custom JavaScript predicate. |
Multi-stub responses (cycle through)
If a stub has multiple responses, Mountebank cycles through them in order on subsequent matching requests:
{
"stubs": [{
"predicates": [{ "equals": { "method": "GET", "path": "/poll" } }],
"responses": [
{ "is": { "statusCode": 202 } },
{ "is": { "statusCode": 202 } },
{ "is": { "statusCode": 200, "body": "DONE" } }
]
}]
}Three calls: 202, 202, 200, then it cycles back. Useful for modeling polling endpoints.
Proxying for record-playback
Set up an imposter as a proxy to a real upstream:
{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{ "matches": { "path": ".*" } }],
"responses": [{
"proxy": {
"to": "https://real-upstream.example.com",
"mode": "proxyOnce"
}
}]
}]
}| Mode | Behavior |
|---|---|
proxyOnce | First request hits upstream; response is stored as a stub; subsequent identical requests replay. |
proxyAlways | Every request hits upstream; every response is stored. |
proxyTransparent | Pass-through; nothing recorded. |
proxyOnce is the canonical record-playback workflow - run tests once against a real upstream to populate the imposter, then run forever offline.
Test framework integration
For Node.js test suites, use the mountebank npm package programmatically:
import mb from 'mountebank';
const mbServer = await mb.create({ port: 2525, allowInjection: true });
// POST imposter via fetch / axios / the mb client lib
await fetch('http://localhost:2525/imposters', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ port: 4545, protocol: 'http', stubs: [...] }),
});
// Run tests against http://localhost:4545
// Tear down
await fetch('http://localhost:2525/imposters/4545', { method: 'DELETE' });
await mbServer.close();CI integration
# .github/workflows/integration.yml
- name: Start Mountebank
run: |
npx -p @mbtest/mountebank mb start &
npx wait-on http://localhost:2525
- name: Seed imposters
run: bash scripts/seed-mountebank.sh
- run: npm test
- name: Stop Mountebank
if: always()
run: pkill -f 'mountebank' || trueFor a more robust pattern, run Mountebank in Docker as a sidecar service rather than a background process - kills + cleanup are cleaner.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Hard-coded imposter ports across many tests | Port collisions under parallel CI execution. | Use dynamic ports; capture them from the control API's response. |
| Predicates with regex that match unintended paths | Test passes because the wrong stub responded. | Anchor regexes (^/$); prefer equals over matches when possible. |
allowInjection: true in production-adjacent envs | JS injection is powerful; allows arbitrary code execution. | Only enable for local / CI; never on a shared mock server. |
| Forgetting to delete imposters between test runs | Stale imposters persist across runs; tests interfere. | DELETE /imposters/<port> in test teardown OR restart Mountebank. |
Recording in proxyAlways mode and committing the captures | Captures may include real PII / tokens. | Use proxyOnce; review captured stubs before committing; scrub PII via JSON Schema or jq pre-commit. |