- Using predicates to send different responses for different requests
- Simplifying predicates on JSON request bodies
- Using XPath to simplify predicates on XML request bodies
How Mountebank matches the request against each stub's predicates?
POST /products?page=2&itemsPerPage=2 HTTP/1.1
Host: api.petstore.com
Content-Type: application/json
{
"key": "asdul7890"
}- Path
- HTTP Method
- HTTP Header
- Query
- Body
- Plan Text
- JSON
- XML
matches-predicate.json
{
"protocol": "http",
"port": 3000,
"stubs": [
{
"predicates": [
{ "equals": { "method": "POST" } },
{ "startsWith": { "body": "smart" } },
{ "endsWith": { "body": "robot" } }
],
"responses": [
{
"is": {
"body": "Hello, smart robot"
}
}
]
}
]
}Mountebank matches
{
"predicates": [
{ "startsWith": { "body": "smart" } },
{ "contains": { "body": "alpha3" } },
{ "endsWith": { "body": "robot" } }
]
}match with regular expressions
{
"predicates": [
{ "matches": { "body": "^text to match" } },
{ "matches": { "body": ".*text to match.*" } },
{ "matches": { "body": "text to match$" } }
]
}path-identifier-predicate.json
{
"protocol": "http",
"port": 3000,
"stubs": [
{
"predicates": [
{
"matches": { "path": "/items/\\d+" }
}
],
"responses": [
{
"is": {
"body": {
"name": "43 Piece Dinner Set",
"price": 12.95,
"quantity": 10
}
}
}
]
}
]
}The metacharacters used here, \d+, represent one or more digits, so the pattern will match /items/123 and /items/2 but not items/robot.
request-field-predicate.json
{
"protocol": "http",
"port": 3000,
"stubs": [
{
"predicates": [
{
"equals": {
"query": { "q": "robot" }
}
}
],
"responses": [
{
"is": {
"body": {
"items": [
{
"name": "TA Robot",
"price": 109.99,
"quantity": 50
},
{
"name": "AlphaBot2",
"price": 71.0,
"quantity": 73
}
]
}
}
}
]
}
]
}deep-equals-predicate.json
{
"deepEquals": {
"query": {
"q": "robot",
"page": 1
}
}
}With this predicate, a query string of ?q=robot&page=1 would match, but a query string of ?q=robot&page=1&sort=desc wouldn’t.
GET /items?q=smart&q=robot
multivalued-field-predicate-01.json
requires exact match
{
"predicates": [
{
"deepEquals": {
"query": { "q": ["smart", "robot"] }
}
}
]
}GET /items?q=smart&q=robot
multivalued-field-predicate-02.json
requires these elements to be present
{
"predicates": [
{
"equals": {
"query": { "q": ["smart", "robot"] }
}
}
]
}There’s one more primitive predicate to look at. The exists predicate tests for either the existence or nonexistence of a request field.
For example, you may decide that for testing purposes, you want to verify that the service handles an HTTP challenge correctly (represented by a 401 sta- tus code) when the Authorization request header is missing, without worrying about whether the credentials stored in the Authorization header are correct, as shown here:
exists-predicate.json
{
"predicates": [
{
"exists": {
"headers": { "Authorization": false }
}
}
],
"responses": [
{
"is": { "statusCode": 401 }
}
]
}conjunction-without-and-predicate.json
{
"predicates": [
{ "startsWith": { "body": "smart" } },
{ "endsWith": { "body": "robot" } }
]
}You can reduce the array to a single element by using the and predicate.
conjunction-with-and-predicate.json
{
"predicates": [
{
"and": [
{ "startsWith": { "body": "smart" } },
{ "endsWith": { "body": "robot" } }
]
}
]
}| Operator | Description |
|---|---|
| equals | Requires the request field to equal the predicate value |
| deepEquals | Performs nested set equality on object request fields |
| contains | Requires the request field to contain the predicate value |
| startsWith | Requires the request field to start with the predicate value |
| endsWith | Requires the request field to end with the predicate value |
| Matches | Requires the request field to match the regular expression provided as the predicate value |
| exists | Requires the request field to exist as a nonempty value (if true) or not (if false) |
| not | Inverts the subpredicate |
| or | Requires any of the subpredicates to be satisfied |
| and | Requires all of the subpredicates to be satisfied |
Predicates are case-insensitive by default.
case-sensitive-predicate.json
{
"predicates": [
{
"equals": {
"query": { "q": "robot" }
}
}
]
}vs
{
"predicates": [
{
"equals": {
"query": { "q": "robot" }
},
"caseSensitive": true
}
]
}POST /items
{ "name": "riderX", "price": 100, "quantity": 50 }direct-json-predicate.json
{
"predicates": [
{
"equals": {
"body": {
"name": "riderX"
}
}
}
],
"responses": [
{
"is": {
"statusCode": 201,
"headers": { "Location": "/items/123" }
}
}
]
}POST /shipping
{
"items": [
{
"name": "TA Robot",
"price": 109.99,
"quantity": 50,
"location": "Bangkok"
},
{
"name": "AlphaBot2",
"price": 71.0,
"quantity": 73,
"location": "Covid-19 Zone"
}
]
}select-a-json-value-with-jsonpath-predicate.json
{
"predicates": [
{ "equals": { "method": "POST" } },
{ "equals": { "path": "/shipping" } },
{
"jsonpath": {
"selector": "$.items[(@.length-1)].location"
},
"equals": { "body": "Covid-19 Zone" }
}
],
"responses": [{ "is": { "statusCode": 400 } }]
}ref: JSONPath syntax
POST /shipping
<items>
<item name="TA Robot">
<price>109.99</price>
<location>Bangkok</location>
</item>
<item name="AlphaBot2">
<price>71.50</price>
<location>Covid-19 Zone</location>
</item>
</items>select-a-xml-value-with-xpath-predicate.json
{
"predicates": [
{ "equals": { "method": "POST" } },
{ "equals": { "path": "/shipping" } },
{
"xpath": {
"selector": "//item[@name='AlphaBot2']/location"
},
"equals": { "body": "Covid-19 Zone" }
}
],
"responses": [{ "is": { "statusCode": 400 } }]
}