<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.0">
<meta name="description" content="The User Guide for using the Brine REST Testing DSL">
<meta name="keywords" content="Brine, Cucumber, REST, DSL">
<meta name="author" content="Matt Whipple">
<title>Brine User Guide</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
[hidden],template{display:none}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
body{margin:0}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}
input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
.antialiased,body{-webkit-font-smoothing:antialiased}
img{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.no-bullet{list-style:none}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite:before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
.clearfix:after,.float-group:after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
.keyseq{color:rgba(51,51,51,.8)}
kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menu{color:rgba(0,0,0,.8)}
b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}
b.button:before{content:"[";padding:0 3px 0 2px}
b.button:after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}
#header:after,#content:after,#footnotes:after,#footer:after{clear:both}
#content{margin-top:1.25em}
#content:before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span:before{content:"\00a0\2013\00a0"}
#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark:before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber:after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
.sect1{padding-bottom:.625em}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}
.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]:before{display:block}
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
.quoteblock .quoteblock blockquote:before{display:none}
.verseblock{margin:0 1em 1.25em 1em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
table.spread{width:100%}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0}
table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0}
table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0}
table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0}
table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot{border-width:1px 0}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none}
ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-check-square-o:first-child,ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{position:relative;top:1px}
ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}
ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}
ul.inline>li>*{display:block}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1{padding-right:.75em;font-weight:bold}
td.hdlist1,td.hdlist2{vertical-align:top}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
.colist>table tr>td:last-of-type{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none}
span.footnote,span.footnoteref{vertical-align:super;font-size:.875em}
span.footnote a,span.footnoteref a{text-decoration:none}
span.footnote a:active,span.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
#footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]:after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
h1,h2{letter-spacing:-.01em}
dt,th.tableblock,td.content{text-rendering:optimizeLegibility}
p,td.content{letter-spacing:-.01em}
p strong,td.content strong{letter-spacing:-.005em}
p,blockquote,dt,td.content{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@media print{@page{margin:1.25cm .75cm}
*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after,a[href^="mailto:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]:after{content:" (" attr(title) ")"}
pre,blockquote,tr,img{page-break-inside:avoid}
thead{display:table-header-group}
img{max-width:100%!important}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
.sect1{padding-bottom:0!important}
.sect1+.sect1{border:0!important}
#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span:before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]:before{display:block}
#footer{background:none!important;padding:0 .9375em}
#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad()</script>
</head>
<body class="article toc2 toc-right">
<div id="header">
<h1><span class="icon"><i class="fa fa-book"></i></span> Brine User Guide</h1>
<div class="details">
<span id="author" class="author">Matt Whipple</span><br>
<span id="email" class="email"><a href="http://github.com/mwhipple">@mwhipple</a></span><br>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_introduction">Introduction</a>
<ul class="sectlevel2">
<li><a href="#_motivation">Motivation</a></li>
<li><a href="#_sample_usage">Sample Usage</a></li>
<li><a href="#_key_features">Key Features</a></li>
</ul>
</li>
<li><a href="#_installation">Installation</a></li>
<li><a href="#_tutorial">Tutorial</a>
<ul class="sectlevel2">
<li><a href="#_selecting_a_root_url">Selecting a ROOT_URL</a></li>
<li><a href="#_a_basic_get">A Basic GET</a></li>
<li><a href="#_a_write_request">A Write Request</a></li>
<li><a href="#_test_response_properties">Test Response Properties</a></li>
</ul>
</li>
<li><a href="#_environment_variables">Environment Variables</a></li>
<li><a href="#_language_conventions">Language Conventions</a>
<ul class="sectlevel2">
<li><a href="#_the_use_of_code_code_s">The use of <code>`</code>s</a></li>
</ul>
</li>
<li><a href="#_selection_and_assertion">Selection and Assertion</a>
<ul class="sectlevel2">
<li><a href="#_selection_modifiers">Selection Modifiers</a></li>
<li><a href="#_chained_assertions">Chained Assertions</a></li>
</ul>
</li>
<li><a href="#_traversal_2">Traversal</a>
<ul class="sectlevel2">
<li><a href="#_cardinality">Cardinality</a></li>
<li><a href="#_expressions">Expressions</a></li>
</ul>
</li>
<li><a href="#_resource_cleanup">Resource Cleanup</a>
<ul class="sectlevel2">
<li><a href="#_step_indicating_resource_to_delete">Step indicating resource to DELETE</a></li>
</ul>
</li>
<li><a href="#_step_reference">Step Reference</a>
<ul class="sectlevel2">
<li><a href="#_request_construction">Request Construction</a></li>
<li><a href="#_cleanup">Cleanup</a></li>
<li><a href="#_assignment">Assignment</a></li>
<li><a href="#_selection">Selection</a></li>
<li><a href="#_assertion">Assertion</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Cucumber DSL for testing REST APIs</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_introduction">Introduction</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_motivation">Motivation</h3>
<div class="paragraph">
<p>REpresentational State Transfer APIs expose their functionality
through combinations of fairly coarse primitives that generally
revolve around the use of transferring data in a standard exchange
format (such as JSON) using HTTP methods and other aspects of the very
simple HTTP protocol. Tests for such an API can therefore be defined
using a domain specific language (DSL) built around those higher level
ideas rather than requiring a general purpose language (the equivalent
of scripted <code>curl</code> commands with some glue code and assertions).
This project provides such a DSL by using select libraries
integrated into Cucumber, where Cucumber provides a test-oriented
framework for DSL creation.</p>
</div>
</div>
<div class="sect2">
<h3 id="_sample_usage">Sample Usage</h3>
<div class="paragraph">
<p>The general usage pattern revolves around construction of a request
and performing assertions against the received response.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">When the request body is assigned:
  """
  {"first_name": "John",
   "last_name": "Smith"}
  """
And a POST is sent to `/users`
Then the value of the response status is equal to `200`
And the value of the response body is including:
  """
  {"first_name": "John",
   "last_name": "Smith"}
  """</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_key_features">Key Features</h3>
<div class="dlist">
<dl>
<dt class="hdlist1">Variable Binding/Expansion</dt>
<dd>
<p>In cases where dynamic data is in the response or is desired for the
request, then values can be bound to identifiers which can then be
expanded using <a href="http://mustache.github.io">Mustache</a> templates in your
feature files.</p>
</dd>
<dt class="hdlist1">Type Transforms</dt>
<dd>
<p>Different types of data can be expressed directly in the feature files
or expanded into variables by using the appropriate syntax for that
type.</p>
</dd>
<dt class="hdlist1">Type Coercion</dt>
<dd>
<p>Related to transforms, a facility to coerce types is also provided. This allows
more intelligent comparison of inputs which have been transformed to a
richer data type with those that have not been transformed (normally strings).
As an example comparing a date/time value with a string will attempt to parse
the string to a date/time so that the values can be compared using the proper semantics.</p>
</dd>
<dt class="hdlist1"><a href="#_resource_cleanup">Resource Cleanup</a></dt>
<dd>
<p>Tests are likely to create resources which should then be cleaned up,
restoring the pre-test state of the system: steps to facilitate this
are provided.</p>
</dd>
<dt class="hdlist1">Authentication</dt>
<dd>
<p>Presently OAuth2 is supported to issue authenticated requests during a
test (likely using a feature <code>Background</code>).</p>
</dd>
<dt class="hdlist1">Request Construction and Response Assertion Step Definitions</dt>
<dd>
<p>The previous features combined with the library of provide steps should
cover all of the functionality needed to exercise and validate all of
the functionality exposed by your REST API.</p>
</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_installation">Installation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Brine is published as <code>brine-dsl</code> on rubygems, the page for which is
at <a href="https://rubygems.org/gems/brine-dsl" class="bare">https://rubygems.org/gems/brine-dsl</a>. The latest version and other
gem metadata can be viewed on that page. Brine can be used by
declaring that gem in your project Gemfile such as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ruby" data-lang="ruby">gem 'brine-dsl', '~&gt; 1.0'</code></pre>
</div>
</div>
<div class="paragraph">
<p>(after version 1.0 is released).</p>
</div>
<div class="paragraph">
<p>Brine can then be "mixed in" to your project (which adds assorted
modules to the <code>World</code> and loads all the step definitions and other
Cucumber magic) by adding the following to your <code>support/env.rb</code> or
other ruby file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ruby" data-lang="ruby">require 'brine'

World(brine_mix)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Select pieces can also be loaded (to be documented). With the above,
feature files should be able to be written and executed without
requiring any additional ruby code.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_tutorial">Tutorial</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We&#8217;ll write some tests against <a href="http://myjson.com/api" class="bare">http://myjson.com/api</a>
(selected fairly arbitrary from the list at  <a href="https://github.com/toddmotto/public-apis" class="bare">https://github.com/toddmotto/public-apis</a>).
The API is being explored for the sake of this tutorial,
which also serves to bolster this library to support the effort.</p>
</div>
<div class="sect2">
<h3 id="_selecting_a_root_url">Selecting a ROOT_URL</h3>
<div class="paragraph">
<p>Brine expects steps to use relative URLs. The feature files specify
the behavior of an API (or multiple APIs), while the root of the
URLs define where that API is, so this is a natural mapping.
More practically, when developing an API it&#8217;s likely to
be promoted across various environments such as
local, qa, stage, and production so having a parameterized root for
the URLs eases this while encouraging inter-environment consistency.</p>
</div>
<div class="paragraph">
<p>For simple cases where all tests are to be run against the same root,
the root url can be specified with the environment variable <code>ROOT_URL</code>,
such as <code>ROOT_URL=https://api.myjson.com/ cucumber</code>, or letting <code>rake</code>
take care of this for you such as:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ruby" data-lang="ruby">Cucumber::Rake::Task.new do
  ENV['ROOT_URL'] = 'https://api.myjson.com/'
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>which could then be called with <code>rake cucumber</code>. The rake approach
can be extended for different tasks for each environment, each
of which sets the appropriate environment variables allowing the
test code itself to follow <a href="https://12factor.net/config">Twelve-Factor App guidelines</a>
where Rake provides sugary convenience.</p>
</div>
</div>
<div class="sect2">
<h3 id="_a_basic_get">A Basic GET</h3>
<div class="paragraph">
<p>Most tests will involve some form of issuing requests and performing assertions
on the responses. Let&#8217;s start with a simple version of that pattern,
testing the response status from a GET request.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Feature: Absent resources return 404s.

  Scenario: A request for a known missing resource.
    When a GET is sent to `/bins/brine-absent`
    Then the value of the response status is equal to `404`</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_a_write_request">A Write Request</h3>
<div class="paragraph">
<p>For POST, PATCH and PUT requests you&#8217;ll normally want to include a request body.
To support this, additional data can be added to the requests before they are sent
(see <a href="#_request_construction">Request Construction</a>).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Feature: A POST returns a 201.

  Scenario: A valid post.
    When the request body is assigned:
      """
      {"name": "boolean-setting",
       "value": true}
      """
    And a POST is sent to `/bins`
    Then the value of the response status is equal to `201`</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_test_response_properties">Test Response Properties</h3>
<div class="paragraph">
<p>The API that was chosen for testing returns the link to the created resource
which is based off of a generated id. That means that the exact response cannot
be verified, but instead property based testing can be done to verify that the
data is sane and therefore likely trustworthy. In this case we
can check that the <code>uri</code> response child matches the expected pattern.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Feature: A POST returns a link to the created resource.

  Scenario: A valid post.
    When the request body is assigned:
      """
      {"name": "boolean-setting",
       "value": true}
      """
    And a POST is sent to `/bins`

    Then the value of the response status is equal to `201`
    And the value of the response body child `uri` is matching `/https://api.myjson.com/bins/\w+/`</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_environment_variables">Environment Variables</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Some Brine behavior can be tuned by passing it appropriate environment variables, listed here.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>BRINE_LOG_HTTP</code></dt>
<dd>
<p>Output HTTP traffic to stdout. Any truthy value will result in request and response
metadata being logged, a value of <code>DEBUG</code> (case insensitive) will also log the bodies.</p>
</dd>
<dt class="hdlist1"><code>BRINE_LOG_BINDING</code></dt>
<dd>
<p>Log values as they are assigned to variables in Brine steps.</p>
</dd>
</dl>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_language_conventions">Language Conventions</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_the_use_of_code_code_s">The use of <code>`</code>s</h3>
<div class="paragraph">
<p>Backticks/grave accents are used as <em>parameter delimiters</em>. It is perhaps
most helpful to think of them in those explicit terms rather than thinking of them
as an alternate <em>quote</em> construct. In particular quoting implies that the parameter
value is a string value, while the step transforms allow for alternate data types.</p>
</div>
<div class="paragraph">
<p><code>`</code>s were chosen as they are less common than
many other syntactical elements and also allow for the use of logically significant
quoting within paremeter values while hopefully avoiding the need for escape artistry
(as used for argument transforms).</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_selection_and_assertion">Selection and Assertion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As tests are generally concerned with performing assertions, a testing DSL should be
able to express the variety of assertions that may be needed. Because these are likely
to be numerous, it could easily lead to duplicated logic or geometric growth of code due
to the combinations of types of assertions and the means to select the inputs for the assertion.</p>
</div>
<div class="paragraph">
<p>To avoid this issue the concepts of selection and assertion are considered separate operations in Brine.
Internally this corresponds to two steps: the first assigns a selector;
the second passes the assertion to that selector which is responsible for applying the assertion against
the selected value(s). In standard step use this will still be expressed as a single step,
and dynamic step definitions are used to split the work appropriately.</p>
</div>
<div class="paragraph">
<p>For example the step:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Then the value of the response body is equal to `foo`</code></pre>
</div>
</div>
<div class="paragraph">
<p>Will be split where the subject of the step (<code>the value of the response body</code>)
defines the selector and the predicate of the step <code>is equal to `foo`</code> defines
the assertion (which is translated to a step such as <code>Then it is equal to `foo`</code>).</p>
</div>
<div class="paragraph">
<p>The result of this is that the assertion steps will always follow a pattern where the subject
resembles <code>the value of &#8230;&#8203;</code> and the predicate always resembles <code>is &#8230;&#8203;</code>. Learning the selection
phrases and the assertion phrases and combining them should be a more efficient and flexible way
to become familiar with the language instead of focusing on the resulting combined steps.</p>
</div>
<div class="paragraph">
<p>The chosen approach sacrifices eloquence for the sake of consistency.
The predicate will always start with <code>is</code> which can lead to awkward language such as
<code>is including</code> rather than simply <code>includes</code>.
The consistency provides additional benefits such as consistent modification:
for instance <code>is not</code> can always be use for negation rather than working out the appropriate
phrasing for a more natural sounding step (let alone the logic).</p>
</div>
<div class="paragraph">
<p>One of the secondary goals of this is that assertion step definitions should very simple to
write and modifiers (such as negation) should be provided for free to those definitions.
As assertion definitions are likely to be numerous and potentially customized, this should help optimize code economy.</p>
</div>
<div class="sect2">
<h3 id="_selection_modifiers">Selection Modifiers</h3>
<div class="paragraph">
<p>To pursue economical flexibility Brine steps attempt to balance step definitions which accommodate variations
while keeping the step logic and patterns fairly simple. Selection steps in particular generally accept some
parameters that affect their behavior. This allows the relatively small number of selection steps to provide
the flexibility to empower the more numerous assertion steps.</p>
</div>
<div class="sect3">
<h4 id="_traversal">Traversal</h4>
<div class="paragraph">
<p>Selection steps can generally target the root of the object specified (such as the response body)
or some nodes within the object if it is a non-scalar value (for instance a child of the response body).
This is indicated in the <a href="#_selection">step reference selection steps</a> by the <code>[$TRAVERSAL]</code> placeholder.
<code>child `$EXPRESSION`</code> or <code>children `$EXPRESSION`</code> can optionally be
inserted at the placeholder to select nested nodes as described in <a href="#_traversal_2">Traversal</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_negation">Negation</h4>
<div class="paragraph">
<p>The selectors also currently handle negation of the associated assertions.
This is potentially counter-intuitive but as previously mentioned the intent is that this
should ease the creation of assertions. If negation is added to a selector that it is expected that
the assertion will <em>fail</em>.</p>
</div>
<div class="paragraph">
<p>Negation will be normally indicated in the <a href="#_selection">step reference selection steps</a> by the presence
of the <code>[not]</code> placeholder. A similar placeholder may be used that is more readable but leads to an equivalent
inversion of the semantics of the statement. To negate the step, the text within the <code>[]</code>s should be inserted
in the indicated position.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Future Versions"></i>
</td>
<td class="content">
Handling this in the selectors is (as mentioned) counter-intuitive and unnecessarily couples the selector
to the assertion. It is currently done for practical reasons but is likely to be replaced in a future version
after (or as part of) the initial port of the library to another platform. When it is replaced, all existing steps
will remain supported through at least one more major revision and most should (most should remain unchanged).
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_chained_assertions">Chained Assertions</h3>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Unsupported Feature"></i>
</td>
<td class="content">
Use at your own risk, this feature is <strong>not presently supported</strong>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For anyone that likes to live on the (relative) edge or if this gathers notable interest&#8230;&#8203;the above also
provides an implicit feature: after a value is selected multiple assertions could be performed against it.
For instance:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Then the value of the response body is equal to `foo`
And it is of the type `String`</code></pre>
</div>
</div>
<div class="paragraph">
<p>Though this may work in simple cases the present design is likely to produce surprising results since
some aspects (such as negation) are handled by the selector so it would be inherited by the conjunctions
even though it wouldn&#8217;t read that way.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_traversal_2">Traversal</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The language exposed by Brine is flat but the data returned by the server is likely
to include deeper data structures such as objects and collections. To allow selection within
such structures a <code>traversal</code> language is embedded within some steps which will be indicated
by the use of the <code>TRAVERSAL</code> placeholder.</p>
</div>
<div class="paragraph">
<p>The traversal language consists of a selected subset of <a href="http://goessner.net/articles/JsonPath/">JsonPath</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="The Selected Subset"></i>
</td>
<td class="content">
The subset of JsonPath functionality has been chosen that is believed to support all needed
test cases without requiring deep familiarity with JsonPath. This may lead to more numerous simple steps
in place of fewer steps that use unsupported expressions. Additionally Brine is intended to be
ported to a range of platforms and so only those steps outlined here will be supported across those platforms.
JsonPath expressions <em>not</em> listed below will not be explicitly disallowed but are not officially supported
(will not be tested and will not be ported to another platform if needed).
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_cardinality">Cardinality</h3>
<div class="paragraph">
<p>Each traversal expression will select <em>all</em> matching nodes which is therefore represented as a collection.
Often, however, only a single node is expected or desired. Therefore the traversal expression will also
be accompanied by a phrase which defines the expected cardinality, normally <code>child</code> vs. <code>children</code>. <code>children</code> will
<em>always</em> return an array while <code>child</code> will return what would be the first element in that array. <code>child</code> should be
used when accessing a specific node within the tree, while <code>children</code> should be used for what amounts to a query
across multiple nodes (such as testing the value of a field for every element in a collection).</p>
</div>
</div>
<div class="sect2">
<h3 id="_expressions">Expressions</h3>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>.$KEY</code></dt>
<dd>
<p>  Access the <code>KEY</code> named child of the starting node. The leading <code>.</code> can be
omitted if at the start of an expression.</p>
</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_resource_cleanup">Resource Cleanup</h2>
<div class="sectionbody">
<div class="paragraph">
<p>All test suites should clean up after themselves as a matter of hygiene and to help enforce test independence
and reproducibility. This is particularly important for this library given that it is likely the systems under test
are likely to remain running; accumulated uncleaned resources are at best a nuisance to have to weed through and
at worst raise some costs or other due to heightened consumption of assorted resources (as opposed to more
ephemeral test environments).</p>
</div>
<div class="paragraph">
<p>Brine therefore provides mechanisms to assist in cleaning up those resources which are created as part of a test run.
A conceptual hurdle for this type of functionality is that it is very unlikely to be part of the feature that is being
specified, and therefore should ideally not be part of the specification. Depending on the functionality
(and arguably the <a href="https://www.martinfowler.com/articles/richardsonMaturityModel.html">maturity</a>) of the
API, most or all of the cleanup can be automagically done based on convention. There are tentative plans to support
multiple techniques for cleaning up resources based on how much can be implicitly ascertained&#8230;&#8203;though presently there
exists only one.</p>
</div>
<div class="sect2">
<h3 id="_step_indicating_resource_to_delete">Step indicating resource to DELETE</h3>
<div class="paragraph">
<p>If the API supports DELETE requests to remove created resources but it is either desirable or necessary to specify
what those resource PATHS are, a step can be used to indicate which resources should be DELETEd upon test completion.</p>
</div>
<div class="paragraph">
<p><em>see <a href="#_cleanup">Cleanup Step Definitions</a></em></p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_step_reference">Step Reference</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_request_construction">Request Construction</h3>
<div class="paragraph">
<p><a href="specs.html#_request_construction"><span class="icon"><i class="fa fa-cogs"></i></span> Specification</a></p>
</div>
<div class="paragraph">
<p>The requests which are sent as part of a test are constructed using
a <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder</a>.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>When a $METHOD is sent to `$PATH`</code></dt>
<dd>
<p>  As every request to a REST API is likely to have a significant
HTTP <code>METHOD</code> and <code>PATH</code>, this step is considered required and is therefore used
to send the built request. This should therefore be the <strong>last</strong> step for any
given request that is being built.</p>
</dd>
<dt class="hdlist1"><code>When the request body is assigned:</code></dt>
<dd>
<p>  The multiline content provided will be assigned to the body of the request.
This will normally likely be the JSON representation of data.</p>
</dd>
<dt class="hdlist1"><code>When the request query parameter `$PARAMETER` is assigned `$VALUE`</code></dt>
<dd>
<p>  Assign <code>VALUE</code> to the request query <code>PARAMETER</code>.
The value will be URL encoded and the key/value pair appended to the URL using
the appropriate <code>?</code> or <code>&amp;</code> delimiter.
The order of the parameters in the resulting URL should be considered undefined.</p>
</dd>
<dt class="hdlist1"><code>When the request header `$HEADER` is assigned `$VALUE`</code></dt>
<dd>
<p>  Assign <code>VALUE</code> to the request header <code>HEADER</code>.
Will overwrite any earlier value for the specified header, including earlier steps or defaults.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_cleanup">Cleanup</h3>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>When a resouce is created at `$PATH`</code></dt>
<dd>
<p>Mark <code>PATH</code> as a resource to DELETE after the test is run. See <a href="#_resource_cleanup">Resource Cleanup</a></p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_assignment">Assignment</h3>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>When `$IDENTIFIER` is assigned `$VALUE`</code></dt>
<dd>
<p>Assigns <code>VALUE</code> to <code>IDENTIFIER</code>.</p>
</dd>
<dt class="hdlist1"><code>When `$IDENTIFIER` is assigned a random string</code></dt>
<dd>
<p>  Assigns a random string (UUID) to <code>IDENTIFIER</code>.
This is particularly useful to assist with test isolation.</p>
</dd>
<dt class="hdlist1"><code>When `$IDENTIFIER` is assigned a timestamp</code></dt>
<dd>
<p>  Assigns to <code>IDENTIFIER</code> a timestamp value representing the instant at
which the step was evaluated.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_selection">Selection</h3>
<div class="paragraph">
<p><a href="specs.html#_selection"><span class="icon"><i class="fa fa-cogs"></i></span> Specification</a></p>
</div>
<div class="paragraph">
<p><em>see <a href="#_selection_and_assertion">Selection and Assertion</a></em></p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>Then the value of the response status is</code></dt>
<dd>
<p>Select the status code of the current HTTP response.</p>
</dd>
<dt class="hdlist1"><code>Then the value of the response body [$TRAVERSAL] is [not]</code></dt>
<dd>
<p>Select the value from the body of the response.</p>
</dd>
<dt class="hdlist1"><code>Then the value of the response body [$TRAVERSAL] does have any element that is [not]</code></dt>
<dd>
<p>Select any (at least one) element from the structure within the response body.</p>
</dd>
<dt class="hdlist1"><code>Then the value of the response body [$TRAVERSAL] has elements which are all</code></dt>
<dd>
<p>Select all elements from the structure within the response body.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_assertion">Assertion</h3>
<div class="paragraph">
<p><a href="specs.html#_assertion"><span class="icon"><i class="fa fa-cogs"></i></span> Specification</a></p>
</div>
<div class="paragraph">
<p><em>see <a href="#_selection_and_assertion">Selection and Assertion</a></em></p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>Then it is equal to `$VALUE`</code></dt>
<dd>
<p>Assert that the current selected value is equivalent to <code>VALUE</code></p>
</dd>
<dt class="hdlist1"><code>Then it is matching `$VALUE`</code></dt>
<dd>
<p>Assert that the current select value matches the regular expression <code>VALUE</code></p>
</dd>
<dt class="hdlist1"><code>Then it is including `$VALUE`</code></dt>
<dd>
<p>Assert that the current select value includes/is a superset of <code>VALUE</code>.</p>
</dd>
<dt class="hdlist1"><code>Then it is empty</code></dt>
<dd>
<p>  Assert that value is empty or null. Any type which is not testable for emptiness
(such as booleans or numbers) will always return false. Null is treated as an empty
value so that it can be treated as such for endpoints that return null in place of empty collections, and non-null empty values can easily be tested for using conjunction.</p>
</dd>
<dt class="hdlist1"><code>Then it is of length `$VALUE`</code></dt>
<dd>
<p>  Assert that the value exposes a length attribute and the value of that
attribute is <code>VALUE</code>.</p>
</dd>
<dt class="hdlist1"><code>Then it is a valid `$TYPE`</code></dt>
<dd>
<p>  Assert that the selected value is a valid instance of a <code>TYPE</code>. Presently this
is focused on standard data types (intially based on those specified by JSON),
but it is designed to handle user specified domain types pending some minor
wiring and documentation. The current supported types are:</p>
<div class="ulist">
<ul>
<li>
<p><code>Object</code>: A JSON style object/associative array</p>
</li>
<li>
<p><code>String</code></p>
</li>
<li>
<p><code>Number</code></p>
</li>
<li>
<p><code>Integer</code></p>
</li>
<li>
<p><code>Array</code></p>
</li>
<li>
<p><code>Boolean</code></p>
</li>
</ul>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2018-05-30 12:55:31 EDT
</div>
</div>
</body>
</html>