mountebank

mountebank - over the wire test doubles


the apothecary

Using JSON Predicates

It is possible to match string JSON fields using string operators, but mountebank finds it clumsy, as you have to either exactly match the whitespace or use a regular expression. Given mountebank's desire for elegance above all else, he supports predicates that treat JSON strings as objects, allowing a fuller range of predicate matching.

JSON predicates follow the same semantics as those obeyed for multi-valued keys described on the main predicates page, like those observed when a querystring has the same key multiple times. Since the selected JSON field can potentially represent an array, most predicates match if any array element matches. deepEquals will require all the values to match (although the order isn't important).

Examples

Let's create an HTTP imposter. To show mountebank's first class support for JSON, we'll also demonstrate passing a JSON object as the http body in the response. We've added a comment field to help explain each predicate. Like all unrecognized fields passed in, mountebank will simply ignore it.

POST /imposters HTTP/1.1
Host: localhost:48451
Accept: application/json
Content-Type: application/json

{
  "port": 4545,
  "protocol": "http",
  "stubs": [
    {
      "responses": [{
        "is": {
          "body": {
            "code": "SUCCESS",
            "author": "J.K. Rowling"
          }
        }
      }],
      "predicates": [
        {
          "equals": { "body": { "title": "Harry Potter" } },
          "caseSensitive": true,
          "comment": "case sensitivity applies to the key as well as the value"
        },
        {
          "equals": { "body": { "title": "POTTER" } },
          "except": "HARRY ",
          "comment": "The except regular expression is removed from the value before matching"
        },
        {
          "matches": { "body": { "title": "^Harry" } }
        },
        {
          "exists": { "body": { "title": true } },
          "comment": "The given JSON key must exist"
        },
        {
          "exists": { "body": { "name": false } },
          "comment": "The given JSON key must NOT exist"
        }
      ]
    }
  ]
}

We'll pass the following HTTP request to test the predicates and confirm we get the expected response:

POST / HTTP/1.1
Host: localhost:4545

{
  "title": "Harry Potter",
  "summary": "Dragons and a boy wizard"
}
HTTP/1.1 200 OK
Connection: close
Date: Thu, 09 Jan 2014 02:30:31 GMT
Transfer-Encoding: chunked

{
    "code": "SUCCESS",
    "author": "J.K. Rowling"
}

Embedded Arrays

mountebank uses the same logic to process arrays as he uses in other predicate operations, which can be summarized as follows:

  • For all operators except deepEquals, at least one element in the array must match. The array syntax can be left off.
  • For deepEquals, all elements of the array have to match, in any order.
  • If you put an array in the predicate definition, all fields must match the array fields in the request, in any order. For deepEquals predicates, the array lengths must also match.

These rules can be explored through the following example:

POST /imposters HTTP/1.1
Host: localhost:48451
Accept: application/json
Content-Type: application/json

{
  "port": 4546,
  "protocol": "http",
  "stubs": [
    {
      "responses": [{
        "is": {
          "body": "Matched all elements exactly"
        }
      }],
      "predicates": [
        {
          "deepEquals": {
            "body": {
              "books": [
                { "title": "The Hobbit" },
                { "title": "Game of Thrones" }
              ]
            }
          }
        }
      ]
    },
    {
      "responses": [{
        "is": {
          "body": "Matched all elements listed"
        }
      }],
      "predicates": [
        {
          "equals": {
            "body": {
              "books": {
                "title": "The Hobbit"
              }
            }
          }
        }
      ]
    }
  ]
}

We'll pass the following HTTP request, leaving off Harry Potter so that all elements match the deepEquals predicate:

POST / HTTP/1.1
Host: localhost:4546

{
  "books": [
    { "title": "Game of Thrones" },
    { "title": "The Hobbit" }
  ]
}
HTTP/1.1 200 OK
Connection: close
Date: Thu, 09 Jan 2014 02:30:31 GMT
Transfer-Encoding: chunked

Matched all elements exactly