mountebank

mountebank - over the wire test doubles


the apothecary

Contracts

To get more details on any field, simply hover your mouse over that field. Purple fields are only meaningful in the response; they will be ignored in any requests.

Showing contract for the resource.

{
 "_links": {
   "imposters": {
      "href": "http://localhost:2525/imposters"
    },
   "config": {
      "href": "http://localhost:2525/config"
    },
   "logs": {
      "href": "http://localhost:2525/logs"
    }
  }
}
{
 "imposters": [
   {
      "protocol": "http",
      "port": 4546,
      "_links": {
        "self": {
          "href": "http://localhost:2525/imposters/4546"
        }
      }
    }
  ]
}
{
 "port": 4545,
 "protocol": "https",
 "name": "imposter contract service",
 "recordRequests": "true",
 "numberOfRequests": "1",
 "key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCrvse04YkxtVGagyvGJCsvv7LTfLK5uR/ZIJKDYCnuF+BqBzM4\nlko8O39vx+Lz9FfF11Xl+CN1aY37YurLYOle3dC/qslSbQDe2TJN7lcVHVssePvc\nO5IExpvNFV5LYtmyCMKJHxpnIprv/trUso5obqzzXhFVPV9SQbFH/snInwIDAQAB\nAoGARywlqLD6YO4qJiULw+4DM6N2oSwBCPRN3XYhIW59kdy1NFtNf7rQgsuJUTJ9\nu+lbYnKNd2LwltyqaS4h7Sx5KRhpFNmMpyVsBf5J2q3fbfmrsXt+emY7XhVTc1NV\nizUWYyxCoTTeMWvN/6NYpPV0lSxq7jMTFVZrWQUMqJclxpECQQDTlGwALtAX1Y8u\nGKsEHPkoq9bhHA5N9WAboQ4LQCZVC8eBf/XH//2iosYTXRNgII2JLmHmmxJHo5iN\nJPFMbnoHAkEAz81osJf+yHm7PBBJP4zEWZCV25c+iJiPDpj5UoUXEbq47qVfy1mV\nDqy2zoDynAWitU7PeHyZ8ozfyribPoR2qQJAVmvMhXKZmvKnLivzRpXTC9LMzVwZ\nV6x/Wim5w8yrG5fZIMM0kEG2xwR3pZch/+SsCzl/0aLLn6lp+VT6nr6NZwJBAMxs\nHrvymoLvNeDtiJFK0nHliXafP7YyljDfDg4+vSYE0R57c1RhSQBJqgBV29TeumSw\nJes6cFuqeBE+MAJ9XxkCQDdUdhnA8HHQRNetqK7lygUep7EcHHCB6u/0FypoLw7o\nEUVo5KSEFq93UeMr3B7DDPIz3LOrFXlm7clCh1HFZhQ=\n-----END RSA PRIVATE KEY-----",
 "cert": "-----BEGIN CERTIFICATE-----\nMIIB6TCCAVICCQCZgxbBD0CG4zANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJV\nUzETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UEChMMVGhvdWdodFdvcmtzMB4X\nDTEzMTIyOTE2NDAzN1oXDTE0MDEyODE2NDAzN1owOTELMAkGA1UEBhMCVVMxEzAR\nBgNVBAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDFRob3VnaHRXb3JrczCBnzANBgkq\nhkiG9w0BAQEFAAOBjQAwgYkCgYEAq77HtOGJMbVRmoMrxiQrL7+y03yyubkf2SCS\ng2Ap7hfgagczOJZKPDt/b8fi8/RXxddV5fgjdWmN+2Lqy2DpXt3Qv6rJUm0A3tky\nTe5XFR1bLHj73DuSBMabzRVeS2LZsgjCiR8aZyKa7/7a1LKOaG6s814RVT1fUkGx\nR/7JyJ8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCPhixeKxIy+ftrfPikwjYo1uxp\ngQ18FdVN1pbI//IIx1o8kJuX8yZzO95PsCOU0GbIRCkFMhBlqHiD9H0/W/GvWzjf\n7WFW15lL61y/kH1J0wqEgoaMrUDjHZvKVr0HrN+vSxHlNQcSNFJ2KdvZ5a9dhpGf\nXOdprCdUUXzSoJWCCg==\n-----END CERTIFICATE-----",
 "mutualAuth": false,
 "defaultResponse": {
    "statusCode": 400,
    "body": "Bad Request",
    "headers": {}
  },
 "stubs": [
    {
     "responses": [
        {
         "is": {
            "statusCode": 201,
            "headers": {
              "Location": "http://example.com/resource"
            },
            "body": "The time is ${TIME}",
            "_mode": "text"
          },
         "repeat": 3,
         "behaviors": [
           { "wait": 500 },
           { "decorate": "config => { config.response.body = config.response.body.replace('${TIME}', 'now'); }" },
           { "shellTransform": "transformResponse" },
           {
              "copy": {
                "from": "body",
                "into": "${NAME}",
                "using": {
                  "method": "xpath",
                  "selector": "//test:name",
                  "ns": { "test": "http://example.com/test" }
                }
              }
            },
           {
              "lookup": {
                "key": {
                  "from": { "headers": "If-Modified-Since" },
                  "using": {
                    "method": "regex",
                    "selector": "(\\w+), (\\d+) (\\w+) (\\d+)",
                    "options": { "ignoreCase": true, "multiline": true }
                  },
                  "index": 2
                },
                "fromDataSource": {
                  "csv": {
                    "path": "values.csv",
                    "keyColumn": "month",
                    "delimiter": ","
                  }
                },
                "into": "${row}"
              }
            }
          ]
        },
        {
         "proxy": {
           "to": "https://www.somesite.com:3000",
           "mode": "proxyAlways",
           "key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCrvse04YkxtVGagyvGJCsvv7LTfLK5uR/ZIJKDYCnuF+BqBzM4\nlko8O39vx+Lz9FfF11Xl+CN1aY37YurLYOle3dC/qslSbQDe2TJN7lcVHVssePvc\nO5IExpvNFV5LYtmyCMKJHxpnIprv/trUso5obqzzXhFVPV9SQbFH/snInwIDAQAB\nAoGARywlqLD6YO4qJiULw+4DM6N2oSwBCPRN3XYhIW59kdy1NFtNf7rQgsuJUTJ9\nu+lbYnKNd2LwltyqaS4h7Sx5KRhpFNmMpyVsBf5J2q3fbfmrsXt+emY7XhVTc1NV\nizUWYyxCoTTeMWvN/6NYpPV0lSxq7jMTFVZrWQUMqJclxpECQQDTlGwALtAX1Y8u\nGKsEHPkoq9bhHA5N9WAboQ4LQCZVC8eBf/XH//2iosYTXRNgII2JLmHmmxJHo5iN\nJPFMbnoHAkEAz81osJf+yHm7PBBJP4zEWZCV25c+iJiPDpj5UoUXEbq47qVfy1mV\nDqy2zoDynAWitU7PeHyZ8ozfyribPoR2qQJAVmvMhXKZmvKnLivzRpXTC9LMzVwZ\nV6x/Wim5w8yrG5fZIMM0kEG2xwR3pZch/+SsCzl/0aLLn6lp+VT6nr6NZwJBAMxs\nHrvymoLvNeDtiJFK0nHliXafP7YyljDfDg4+vSYE0R57c1RhSQBJqgBV29TeumSw\nJes6cFuqeBE+MAJ9XxkCQDdUdhnA8HHQRNetqK7lygUep7EcHHCB6u/0FypoLw7o\nEUVo5KSEFq93UeMr3B7DDPIz3LOrFXlm7clCh1HFZhQ=\n-----END RSA PRIVATE KEY-----",
           "cert": "-----BEGIN CERTIFICATE-----\nMIIB6TCCAVICCQCZgxbBD0CG4zANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJV\nUzETMBEGA1UECBMKU29tZS1TdGF0ZTEVMBMGA1UEChMMVGhvdWdodFdvcmtzMB4X\nDTEzMTIyOTE2NDAzN1oXDTE0MDEyODE2NDAzN1owOTELMAkGA1UEBhMCVVMxEzAR\nBgNVBAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDFRob3VnaHRXb3JrczCBnzANBgkq\nhkiG9w0BAQEFAAOBjQAwgYkCgYEAq77HtOGJMbVRmoMrxiQrL7+y03yyubkf2SCS\ng2Ap7hfgagczOJZKPDt/b8fi8/RXxddV5fgjdWmN+2Lqy2DpXt3Qv6rJUm0A3tky\nTe5XFR1bLHj73DuSBMabzRVeS2LZsgjCiR8aZyKa7/7a1LKOaG6s814RVT1fUkGx\nR/7JyJ8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQCPhixeKxIy+ftrfPikwjYo1uxp\ngQ18FdVN1pbI//IIx1o8kJuX8yZzO95PsCOU0GbIRCkFMhBlqHiD9H0/W/GvWzjf\n7WFW15lL61y/kH1J0wqEgoaMrUDjHZvKVr0HrN+vSxHlNQcSNFJ2KdvZ5a9dhpGf\nXOdprCdUUXzSoJWCCg==\n-----END CERTIFICATE-----",
           "ciphers": "RC4-MD5",
           "secureProtocol": "TLSv1_1_method",
           "passphrase": "LetMeIn!",
           "addWaitBehavior": "true",
           "addDecorateBehavior": "(request, response) => { response.body = response.body.replace('${TIME}', 'now'); }",
           "predicateGenerators": [
              {
                "matches": {
                  "path": true
                },
                "caseSensitive": true,
                "except": "^The^",
                "jsonpath": {
                  "selector": "$..book"
                },
                "xpath": {
                  "selector": "//book/@title",
                  "ns": {
                    "isbn": "http://schemas.isbn.org/ns/1999/basic.dtd"
                  }
                },
                "inject": "function (config) { return []; }",
                "ignore": { "query": "startDate" }
              }
            ],
           "injectHeaders": {
              "X-Custom-Header": "Served by mountebank"
            }
          }
        },
        {
         "inject": "function (config) { config.callback({ body: 'It worked!' }); }"
        }
      ],
     "predicates": [
        {
         "equals": {
            "body": "value"
          },
         "caseSensitive": true,
         "except": "^The ",
         "jsonpath": {
            "selector": "$..book"
          },
         "xpath": {
            "selector": "//book/@title",
            "ns": {
              "isbn": "http://schemas.isbn.org/ns/1999/basic.dtd"
            }
          }
        },
        {
         "inject": "function (config) { return config.request.body.length < 100; }"
        }
      ],
     "matches": [
        {
          "timestamp": "2014-01-09T02:30:31.043Z",
          "request": {
            "requestFrom": "::ffff:127.0.0.1:60523",
            "method": "POST",
            "path": "/imposters",
            "query": {},
            "headers": {
              "accept": "text/plain",
              "host": "localhost:4545",
              "content-type": "application/xml",
              "connection": "keep-alive",
              "transfer-encoding": "chunked"
            },
            "body": "<books><book title='The value' /></books>"
          },
          "response": {
            "statusCode": 201,
            "headers": {
              "Location": "http://example.com/resource"
            },
            "body": "The time is now",
            "_mode": "text"
          }
        }
      ]
    }
  ],
 "endOfRequestResolver": {
    "inject": "function (config) { return config.request.length >= 100; }"
  },
 "requests": [
    {
      "timestamp": "2014-01-09T02:30:31.043Z",
      "requestFrom": "::ffff:127.0.0.1:60523",
      "method": "POST",
      "path": "/imposters",
      "query": {},
      "headers": {
        "accept": "text/plain",
        "host": "localhost:4545",
        "content-type": "text/plain",
        "connection": "keep-alive",
        "transfer-encoding": "chunked"
      },
      "body": "Just checking"
    }
  ],
 "_links": {
    "self": {
      "href": "http://localhost:2525/imposters/4545"
    }
  }
}
{
 "index": 1,
 "stub": {
    "responses": [{
      "is": {
        "body": "Hello, world!"
      }
    }]
  }
}
{
 "responses": [{
    "is": {
      "body": "Hello, world!"
    }
  }]
}
{
  "stubs": [
   {
      "responses": [{
        "is": {
          "body": "Hello, world!"
        }
      }]
    }
  ]
}
{
 "version": "1.4.1",
 "options": {
    "port": 2525,
    "pidfile": "mb.pid",
    "logfile": "mb.log",
    "loglevel": "info",
    "configfile": "",
    "allowInjection": false,
    "mock": true,
    "debug": true
  },
 "process": {
   "nodeVersion": "v6.9.1",
   "architecture": "x64",
    "platform": "darwin",
   "rss": 29822976,
    "heapTotal": 18635008,
    "heapUsed": 9294352,
   "uptime": 16,
   "cwd": "/Users/bbyars/src/mountebank"
  }
}
{
 "logs": [
    {
      "level": "info",
      "message": "[mb:2525] mountebank v1.4.1 (node v4.2.0) now taking orders - point your browser to http://localhost:2525 for help",
      "timestamp": "2015-10-20T02:24:41.818Z"
    },
    {
      "level": "info",
      "message": "[mb:2525] Adios - see you soon?",
      "timestamp": "2015-10-20T02:31:38.109Z"
    }
  ]
}

An array of imposter objects.

A single imposter object.

By default, the fields shown are the only ones returned. Use additional query parameters to return the full imposter definition, and optionally remove proxies for subsequent replays

More information: imposter contract

The port to run the imposter on.

Optional. Defaults to a randomly assigned port that will be returned in the response

On Linux and Mac, requires sudo to set to a privileged port (1-1023)

Defines the protocol that the imposter will respond to. Valid values are:

Optional. Allows you to provide a descriptive name that will show up in the logs and the imposters UI.

Optional. If set to true, the server will record requests received, for mock verification purposes.

The number of requests to this imposter

Set recordRequests to true on the imposter to also record the details of each request in the requests array below

Optional. Allows you to pass in an SSL private key for creating an https server (this field only applies to https). Must be a PEM-formatted string. Defaults to a built-in private key.

More information: https

Optional. Allows you to pass in an SSL certificate for creating an https server (this field only applies to https). Must be a PEM-formatted string. Defaults to a built-in self-signed certificate.

More information: https

Optional. If set to true, the server will request a client certificate. Since the goal is simply to virtualize a server requiring mutual auth, invalid certificates will not be rejected.

More information: https

Optional. Allows you to override the default response that mountebank sends back if no predicate matches a request. Even if a predicate does match but the response isn't fully specified, these values get merged in to complete the response.

More information: http, https, and tcp

A set of behaviors used to generate a response for an imposter. An imposter can have 0 or more stubs, each of which are associated with different predicates and support different responses.

You would use multiple stubs for an imposter if the types of response you return depends on something in the request, matched with a predicate.

An array of responses to return for this stub. The responses array defines a circular buffer - every time the stub is used for the request, the first response is pulled from the front of the responses array, evaluated, and pushed to the back of the array. This elegantly does what you want. In the common case, when you always want to return the same response, you just add one response to the array. More complex scenarios will require that the same endpoint returns a sequence of different responses for the same predicates. Simply add them all to the array in order. When the sequence finishes, it will start over. More complexity can be added by simply adding more responses to the array without complicating the contract.

Each response is defined by one and only one response type. This defines the type of response generated. The following response types are supported, and described in more detail in the contract example to the left as well as the links provided below.

  • is: A canned response
  • proxy: A record/replay response
  • inject: A scripted response

More informaton: stubs

The is response type represents a canned response that you define. The response fields will be those defined with each protocol according to the links below (the http response fields are shown in this example). smtp stubbing is not supported.

You do not need to fill in every response field; mountebank will merge the specified response fields with the response defaults (see the protocol pages below for the defaults).

More information: stubs

Causes the response to repeat a given number of times before returning the next response in the responses array.

More information: stub responses

Behaviors alter the generated response in some way. The following behaviors are supported:

  • wait - adds latency to the response
  • decorate - post-processes the response using a JavaScript function
  • shellTransform - post-processes the response using a command line application
  • copy - copies a value from the request into the response
  • lookup - lookups values from an external data source

More information: behaviors

Adds latency to a response by waiting a specified number of milliseconds before sending the response.

More information: behaviors

Post-processes the response before sending it. Since post-processing requires JavaScript injection, the --allowInjection flag must be passed. Post-processing opens up a world of opportunities - you can use a decorate behavior to add data to a proxied response or substitute dynamic data into the response, for example. The value passed into the decorate behavior is a JavaScript function that can take up to an object containing the request, the response, and a logger. You can either mutate the response passed in (and return nothing), or return an altogether new response.

JavaScript injection requires the --allowInjection command line flag to execute.

More information: behaviors

Post-processes the response before sending it. Unlike a decorate, the post-processing occurs through a command line application, which still requires the --allowInjection flag to be set. The command line application will be passed the request JSON and the response JSON through environment variables, and should write the transformed response JSON to stdout.

Shell execution requires the --allowInjection command line flag to execute.

More information: behaviors

Post-processes the response by replacing the given tokens with values from the specified request fields. You can select the appropriate values from the request using regular expressions, xpath, or jsonpath.

More information: behaviors

Post-processes the response by looking up data from an external data source using a key selected from the request. The given tokens in the response are replaced with the looked up values. You can select the appropriate keys from the request using regular expressions, xpath, or jsonpath.

More information: behaviors

The response is generated by proxying the request to a different server. You can configure mountebank to return the saved response on subsequent requests based on certain conditions within the request.

More information: proxies

Represents the destination to send the request. This should be a URL with the protocol and host but without a path (as shown to the left). http and https imposters can both proxy to http and https. tcp proxies can only proxy to tcp (e.g. tcp://someserver:3000).

More information: proxies

Defines the replay behavior of the proxy. Proxy responses work by creating new elements in the stubs array. This field determines whether that newly created stub will be before or after the current stub in the array. There are two options:

  • proxyOnce - always records the proxied call in the stubs array in front of itself, so the same call is never proxied twice.
  • proxyAlways - saves the proxied call after itself in the stubs array. This allows you to capture different responses for the same call. You can later replay proxyAlways stubs by issuing a GET or DELETE to the imposter with the removeProxies and replayable query params, and re-POSTing the imposter.

More information: proxies

In situations where the destination expects to use SSL mutual authentication, it will request a client certificate. This field can contain the PEM-formatted client private key.

More information: proxies

In situations where the destination expects to use SSL mutual authentication, it will request a client certificate. This field can contain the PEM-formatted client certificate.

More information: proxies

In nearly all cases, you'll never need to use this field. However if you need to proxy to an older HTTPS server using insecure ciphers that are no longer used, you can override the cipher using one of the standard formats.

More information: proxies

A legacy mechanism to select the TLS protocol to use. Learn more at the node documentation.

More information: proxies

The shared passphrase for a private key

More information: proxies

When this field is true, mountebank will add latency to the saved responses that mirrors the time the actual proxied call took. This is useful in load testing when you want to virtualize downstream services but keep the latency of those services.

More information: proxies and the wait behavior

When present, adds the given function as a decorate behavior to each newly created response.

JavaScript injection requires the --allowInjection command line flag to execute.

More information: proxies

Allows you to add or override headers returned from the proxy.

More information: proxies

An array of objects that defines how the predicates for new stubs are created. Proxy responses work by creating new elements in the stubs array, either before the current stub (if mode is proxyOnce) or after the current stub (if mode is proxyAlways). Each element in the predicateGenerators array will create a single element in the predicates array for newly created stub.

Each generator should contain a matches field containing all the request elements you want to create predicates for. You can configure the newly created predicate with standard predicate parameters. The created predicates will be generally be deepEquals predicates.

Alternatively, if you need more fine-grained control, you can use an inject field containing a JavaScript function to create the predicates.

More information: proxies

mountebank is scriptable, which allows you to use JavaScript to craft the response you want in situations where canned responses and proxy responses are not sufficient. The injected function takes four parameters as shown on the left:

  • request - the protocol-specific request object
  • state - initially an empty object, the same instance will be passed into every response injection function within the same imposter. You can use it to add any cross-response state you need to save.
  • logger - mountebank's logger, with standard debug, info, warn, and error functions
  • callback - For asynchronous use only, execute (with the response as a parameter) to return

Injection can by synchronous or asnychronous. Simply return a value representing the response for synchronous execution. For asynchronous execution, pass the response object into the callback function.

JavaScript injection requires the --allowInjection command line flag to execute.

More information: injection

In the absence of a predicate, a stub always matches, and there's never a reason to add more than one stub to an imposter. Predicates allow imposters to have much richer behavior by defining whether or not a stub matches a request. When multiple stubs are created on an imposter, the first stub that matches is selected.

Each predicate object contains one or more of the request fields as keys. Predicates are added to a stub in an array, and all predicates are AND'd together. The following predicate operators are allowed:

  • equals - The request field matches the predicate
  • deepEquals - Performs nested set equality on the request field, useful when the request field is an object (e.g. the query field in http)
  • contains - The request field contains the predicate
  • startsWith - The request field starts with the predicate
  • endsWith - The request field ends with the predicate
  • matches - The request field matches the JavaScript regular expression defined with the predicate.
  • exists - If true, the request field must exist. If false, the request field must not exist.
  • not - Inverts a predicate
  • or - Logically or's two predicates together
  • and - Logically and's two predicates together
  • inject - Injects JavaScript to decide whether the request matches or not.

More information: predicates

A type of predicate that requires the given request fields to equal the predicate value. By default, mountebank uses a case-insensitive equality.

More information: predicates

If true, mountebank will require the request field to satisfy the predicate value in a case sensitive comparison.

More information: predicates

Defines a regular expression that is stripped out of the request field before matching.

More information: predicates

It is common to want to use predicates on JSON response bodies, but annoying to treat the JSON as simple text with many of the predicates. mountebank uses the json predicate parameter to narrow the scope of the predicate value to a value matched by the json selector.

More information: jsonpath

It is common to want to use predicates on XML response bodies, but annoying to treat the XML as simple text with many of the predicates. mountebank uses the xpath predicate parameter to narrow the scope of the predicate value to a value matched by the xpath selector.

More information: xpath

When none of the built-in predicates are sufficient, mountebank allows you to script the predicate. The injected function accepts the following parameters:

  • request - the full request object
  • logger - mountebank's logger, with standard debug, info, warn, and error functions

Return true to pass the predicate.

JavaScript injection requires the --allowInjection command line flag to execute.

More information: injection

An array of all activity by this stub, useful in troubleshooting why a stub is or is not responding (generally for debugging your predicates). Each object will contain the incoming request>, the outgoing response, and a timestamp.

The matches array only shows up in the response if the --debug command line parameter is passed.

The tricky bit about using the raw TCP protocol is knowing when a request ends. Application protocols give some way of determining this, such as the Content-Length header in HTTP or by embedding the message length in binary protocols. By default, mountebank assumes each packet represents a separate request, which typically limits the size of each request to somewhere between 1500 and 64k bytes (you'll get a larger payload using the loopback interface on localhost, but lower level protocols like Ethernet will force a smaller payload over the network). This strategy works for many scenarios as simple serialized requests tend to fall below this size, but breaks down with large requests.

mountebank allows you the flexibility to determine when a request ends using the endOfRequestResolver at the imposter level. The value is a JavaScript function that takes a parameter that represents all request data captured from all packets so far. If the imposter is in text mode, the parameter will be a string; in binary it will be a node.js Buffer object. mountebank's logger object is also passed as an optional second parameter to assist in troubleshooting.

JavaScript injection requires the --allowInjection command line flag to execute.

More information: tcp

mountebank will save off all requests to the imposter for mock verification. By retrieving the imposter, your client code can determine if an expected service call was in fact made.

More information: mocks

The array index to add the stub. Stubs are always evaluated in array order, and the first stub whose predicates match the request will be used. If you leave this value off the request, the stub will be added to the end of the array.

The stub to add. See the imposter contract for more information.

More information: imposter contract

The stub to add. See the imposter contract for more information.

More information: imposter contract

The stub to add. See the imposter contract for more information.

More information: imposter contract

The mountebank version

The command line options used to start mb.

More information: command line options

Information about the running mb process

The version of node.js

The operating system and architecture of the machine running mountebank.

The memory (in bytes) used by mountebank, and heap usage of the V8 JavaScript engine.

The number of seconds this process has been running.

The current directory, used to start mb

An array of all log statements captured in the current log file.