Parameter | Type | Description |
---|---|---|
copy |
An object | An object specifying the request field and response token, as well as a way of selecting the value from the request field |
copy.from |
A string or an object | The name of the request field to copy from, or, if the request field is an object,
then an object specifying the path to the request field. For example,
and
are both valid. |
copy.into |
A string | The token to replace in the response with the selected request value. There is
no need to specify which field in the response the token will be in; all response
tokens will be replaced in all response fields. Sometimes, the request selection
returns multiple values. In those cases, you can add an index to the token, while
the unindexed token represents the first match. For example, if you specify
as your token configuration, then both
${NAME} and ${NAME}[0] will be replaced by the first match,
${NAME}[1] will be replaced by the second match, and so on. |
copy.using |
An object | The configuration needed to select values from the response |
copy.using.method |
An string | The method used to select the value(s) from the request. Allowed values are regex ,
xpath , and jsonpath . |
copy.using.selector |
An string | The selector used to select the value(s) from the request. For a regex , this would
be the pattern, and the replacement value will be the entire match. Match groups using parentheses are supported
and can be replaced using indexed tokens as described in the copy[].into description.
xpath and jsonpath selectors work on XML and JSON documents. If the request
value does not match the selector (including through XML or JSON parsing errors), nothing is replaced. |
copy.using.ns |
An object | For xpath selectors, the ns object maps namespace aliases to URLs |
copy.using.options |
An object | For regex selectors, the options object describes the regular expression options |
copy.using.options.ignoreCase |
A boolean | Uses a case-insensitive regular expression |
copy.using.options.multiline |
A boolean | Uses a multiline regular expression |
The copy
behavior supports dynamically replacing values in the response with something that
comes from the request. It relies on you adding tokens of your own choosing into the response fields you want
replaced. We'll look at the following examples:
The following example shows multiple regular expression matches on request fields to copy into the response.
POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json
{
"port": 8585,
"protocol": "http",
"stubs": [
{
"responses": [
{
"is": {
"statusCode": "${code}",
"headers": {
"X-Test": "${header}"
},
"body": "The request name was ${name}. Hello, ${name}!"
},
"behaviors": [
{
"copy": {
"from": "path",
"into": "${code}",
"using": { "method": "regex", "selector": "\\d+" }
}
},
{
"copy": {
"from": { "headers": "X-Request" },
"into": "${header}",
"using": { "method": "regex", "selector": ".+" }
}
},
{
"copy": {
"from": { "query": "name" },
"into": "${name}",
"using": {
"method": "regex",
"selector": "MOUNT\\w+$",
"options": { "ignoreCase": true }
}
}
}
]
}
]
}
]
}
This example shows off many of the options of the copy
behavior. For example,
we can plug tokens into any of the response fields (including the statusCode
), and
it shows how to navigate object request fields, like the name
querystring
parameter. It shows an example of using regular expressions options to get a case-insensitive
regular expression to capture the name
query parameter. It also shows matching multiple
request fields using an array of copy
configurations. Let's see what happens when we
craft a request to match all of those selectors:
GET /statusCode/400?ignore=this&name=mountebank HTTP/1.1
Host: localhost:8585
X-REQUEST: Header value
HTTP/1.1 400 Bad Request
X-Test: Header value
Connection: close
Date: Thu, 28 Dec 2016 11:37:31 GMT
Transfer-Encoding: chunked
The request name was mountebank. Hello, mountebank!
DELETE /imposters/8585 HTTP/1.1
Host: localhost:<%= port %>
The following example shows a simple namespaced xpath match to grab the first title
field in
an XML document and copy it into the BOOK
response token.
POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json
{
"port": 8586,
"protocol": "http",
"stubs": [
{
"responses": [
{
"is": {
"body": "Have you read BOOK?"
},
"behaviors": [
{
"copy": {
"from": "body",
"into": "BOOK",
"using": {
"method": "xpath",
"selector": "//isbn:title",
"ns": {
"isbn": "http://schemas.isbn.org/ns/1999/basic.dtd"
}
}
}
}
]
}
]
}
]
}
The ns
object map is optional and can be ignored if your xpath selector doesn't depend on
namespaces. It doesn't matter how many name
elements exist in the XML. Without using indexed
tokens, only the first match will be used:
POST /names HTTP/1.1
Host: localhost:8586
<books xmlns:isbn="http://schemas.isbn.org/ns/1999/basic.dtd">
<book>
<isbn:title>Game of Thrones</isbn:title>
<isbn:summary>Dragons and political intrigue</isbn:summary>
</book>
<book>
<isbn:title>Harry Potter</isbn:title>
<isbn:summary>Dragons and a boy wizard</isbn:summary>
</book>
<book>
<isbn:title>The Hobbit</isbn:title>
<isbn:summary>A dragon and short people</isbn:summary>
</book>
</books>
HTTP/1.1 200 OK
Connection: close
Date: Thu, 28 Dec 2016 11:37:31 GMT
Transfer-Encoding: chunked
Have you read Game of Thrones?
DELETE /imposters/8586 HTTP/1.1
Host: localhost:<%= port %>
The following example translates the XML example above into JSON. To make it more interesting,
we'll show it using the tcp
protocol
POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json
{
"port": 8587,
"protocol": "tcp",
"stubs": [
{
"responses": [
{
"is": {
"data": "Have you read BOOK?"
},
"behaviors": [
{
"copy": {
"from": "data",
"into": "BOOK",
"using": {
"method": "jsonpath",
"selector": "$..title"
}
}
}
]
}
]
}
]
}
Again, by default only the first match will be used:
echo '{
"books": [
{
"title": "Game of Thrones",
"summary": "Dragons and political intrigue"
},
{
"title": "Harry Potter",
"summary": "Dragons and a boy wizard"
},
{
"title": "The Hobbit",
"summary": "A dragon and short people"
}
]
}' | nc localhost 8587
Have you read Game of Thrones?
DELETE /imposters/8587 HTTP/1.1
Host: localhost:<%= port %>
Finally, let's show an example that uses multiple matches for a given selector. To show that the same approach works for multiple selection methods, we'll show it for both regular expressions and jsonpath:
POST /imposters HTTP/1.1
Host: localhost:<%= port %>
Accept: application/json
Content-Type: application/json
{
"port": 8588,
"protocol": "http",
"stubs": [
{
"responses": [
{
"is": {
"body": "${BOOK}[1]: ${SUMMARY}[0]\n${BOOK}[2]: ${SUMMARY}[1]\n${BOOK}[3]: ${SUMMARY}[2]"
},
"behaviors": [
{
"copy": {
"from": "body",
"into": "${SUMMARY}",
"using": {
"method": "jsonpath",
"selector": "$..summary"
}
}
},
{
"copy": {
"from": { "query": "books" },
"into": "${BOOK}",
"using": {
"method": "regex",
"selector": "([^,]+),([^,]+),(.+)$"
}
}
}
]
}
]
}
]
}
Note the mismatched indexes between the two selection methods. This is because we use the standard regular
expression semantics around matched groups, which is that the first element in the matches will be the entire
matched expression, the second element will be the first parenthesized match group, and so on. Also note that
${SUMMARY}[0]
and ${SUMMARY}
will be treated identically. We'll
trigger the substitutions with the following request:
POST /?books=Game%20of%20Thrones,Harry%20Potter,The%20Hobbit HTTP/1.1
Host: localhost:8588
{
"books": [
{
"title": "Game of Thrones",
"summary": "Dragons and political intrigue"
},
{
"title": "Harry Potter",
"summary": "Dragons and a boy wizard"
},
{
"title": "The Hobbit",
"summary": "A dragon and short people"
}
]
}
HTTP/1.1 200 OK
Connection: close
Date: Thu, 28 Dec 2016 11:37:31 GMT
Transfer-Encoding: chunked
Game of Thrones: Dragons and political intrigue
Harry Potter: Dragons and a boy wizard
The Hobbit: A dragon and short people
DELETE /imposters/8588 HTTP/1.1
Host: localhost:<%= port %>