bbyars/mountebank

View on GitHub
src/views/docs/api/json.ejs

Summary

Maintainability
Test Coverage
<%
title = 'json'
description = 'Using JSON in mountebank predicates'
%>

<%- include('../../_header') -%>

<h1>Using JSON Predicates</h1>

<p>It is possible to match <code>string</code> 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.</p>

<p>JSON predicates follow the same semantics as those obeyed for multi-valued keys described on the
    <a href='/docs/api/predicates'>main predicates page</a>, 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 <em>any</em> array element matches.  <code>deepEquals</code> will require all the values to match
    (although the order isn't important).</p>

<h2>Examples</h2>

<p>Let's create an HTTP imposter.  To show mountebank's first class support for JSON, we'll also
    demonstrate <a href='/docs/protocols/http#inline-json-response-bodies'>passing a JSON object
    as the <code>http body</code></a> in the response.  We've added a <code>comment</code> field
    to help explain each predicate.  Like all unrecognized fields passed in, mountebank will
    simply ignore it.</p>

<testScenario name='json'>
    <step type='http'>
<pre><code>POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json

{
  "port": 4545,
  "protocol": "http",
  "stubs": [
    {
      "responses": [{
        "is": {
          "body": <strong class='highlight1'>{
            "code": "SUCCESS",
            "author": "J.K. Rowling"
          }</strong>
        }
      }],
      "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"
        }
      ]
    }
  ]
}</code></pre>
    </step>

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

    <step type='http'>
<pre><code>POST / HTTP/1.1
Host: localhost:4545

{
  "title": "Harry Potter",
  "summary": "Dragons and a boy wizard"
}</code></pre>

        <assertResponse>
<pre><code>HTTP/1.1 200 OK
Connection: close
Date: <volatile>Thu, 09 Jan 2014 02:30:31 GMT</volatile>
Transfer-Encoding: chunked

<strong class='highlight1'>{
    "code": "SUCCESS",
    "author": "J.K. Rowling"
}</strong></code></pre>
        </assertResponse>
    </step>

<h3>Embedded Arrays</h3>

<p>mountebank uses the same logic to <a href='/docs/api/predicates#array-match'>process arrays</a> as he
uses in other predicate operations, which can be summarized as follows:</p>

<ul class='bullet-list'>
    <li>For all operators except <code>deepEquals</code>, at least one element in the array must match.
    The array syntax can be left off.</li>
    <li>For <code>deepEquals</code>, all elements of the array have to match, in any order.</li>
    <li>If you put an array in the predicate definition, all fields must match the array fields in the
    request, in any order. For <code>deepEquals</code> predicates, the array lengths must also match.</li>
</ul>

<p>These rules can be explored through the following example:</p>

    <step type='http'>
<pre><code>POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json

{
  "port": 4546,
  "protocol": "http",
  "stubs": [
    {
      "responses": [{
        "is": {
          "body": "<strong class='highlight1'>Matched all elements exactly</strong>"
        }
      }],
      "predicates": [
        {
          "deepEquals": {
            "body": {
              "books": [
                { "title": "The Hobbit" },
                { "title": "Game of Thrones" }
              ]
            }
          }
        }
      ]
    },
    {
      "responses": [{
        "is": {
          "body": "<strong class='highlight2'>Matched all elements listed</strong>"
        }
      }],
      "predicates": [
        {
          "equals": {
            "body": {
              "books": {
                "title": "The Hobbit"
              }
            }
          }
        }
      ]
    }
  ]
}</code></pre>
    </step>

<p>We'll pass the following HTTP request, leaving off <em>Harry Potter</em> so that all elements match the
    <code>deepEquals</code> predicate:</p>

    <step type='http'>
<pre><code>POST / HTTP/1.1
Host: localhost:4546

{
  "books": [
    { "title": "Game of Thrones" },
    { "title": "The Hobbit" }
  ]
}</code></pre>

        <assertResponse>
<pre><code>HTTP/1.1 200 OK
Connection: close
Date: <volatile>Thu, 09 Jan 2014 02:30:31 GMT</volatile>
Transfer-Encoding: chunked

<strong class='highlight1'>Matched all elements exactly</strong></code></pre>
        </assertResponse>
    </step>

    <step type='http'>
<code class='hidden'>DELETE /imposters/4545 HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json</code>
    </step>

    <step type='http'>
<code class='hidden'>DELETE /imposters/4546 HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json</code>
    </step>
</testScenario>

<%- include('../../_footer') -%>