Mocks for microservice environments
The general rule for matching request to endpoint definition is “take first endpoint that fully matches criteria”. This
means that there can be multiple endpoints with same path
and method
, but different queryString
/headers
/body
criteria. If no match is found a 404
response will be returned.
Note: there is a special priority given to constant path
prefixes over parameterized ones,
see explanation below.
URL path
is the main criteria of matching endpoint to request, and the only required option in endpoint definition.
You can use {{varname}}
syntax to specify a path parameter in any segment of your paths and it will become available
for using in the response template:
endpoints:
- path: "/parameterized/{{myVar}}/someval"
response: 'Here is: {{myVar}}'
With Mockintosh it’s possible to use regular expressions in path segments:
- path: "/match/{{regEx 'prefix-.*'}}/someval"
response: 'regex match: {{request.path}}'
so that a request like curl http://localhost:8001/match/prefix-hello_world/someval
would
return regex match: /match/prefix-hello_world/someval
It’s also possible to use regular expression capture groups in path segments, as templating items:
- path: "/match/{{regEx 'prefix-(.*)' 'myVar'}}/someval"
response: 'regex capture group: {{myVar}}'
so that a request like curl http://localhost:8001/match/prefix-hello_world/someval
would
return regex capture group: hello_world
You can use as many path parameters and regex capture groups you want:
- path: "/parameterized5/text/{{var1}}/{{regEx 'prefix-(.*)-(.*)-suffix' 'var2' 'var3'}}/{{var4}}/{{regEx 'prefix2-(.*)' 'var5'}}"
response: 'var1: {{var1}}, var2: {{var2}}, var3: {{var3}}, var4: {{var4}}, var5: {{var5}}'
regEx
ConversionWhen you define a variable for a portion of a path segment like the example below, it’s automatically detected and converted to a regex function behind the scenes.
E.g.:
- path: "/hello-{{somevar}}/another"
response: 'result: {{somevar}}'
is automatically converted to:
- path: "/{{regEx 'hello\-(.*)' 'somevar'}}/another"
response: 'result: {{somevar}}'
Note: “Automatic regEx
Conversion” is not specific to path
attribute but rather applies to places where templating is possible.
Similar to the Automatic RegEx Conversion mechanic, it’s possible to
match query strings
using the path
attribute. path
can be extended to match the path[?query][#fragment]
portion
of URI(scheme:[//authority]path[?query][#fragment]
)
for practical reasons.
E.g.:
- path: "/search?q={{keyword1}}&s={{keyword2}}"
response: 'result: {{keyword1}} {{keyword2}}'
is automatically converted to:
- path: "/search"
queryString:
q: "{{keyword1}}"
s: "{{keyword1}}"
response: 'result: {{keyword1}} {{keyword2}}'
Note: “Automatic Query String Conversion” is specific to path
attribute.
Even if you specified a path parameter for a certain path segment, static values have a higher priority over named
parameters and regEx
:
endpoints:
- path: "/parameterized/{{myVar}}/someval"
response: 'Here is: {{myVar}}'
- path: "/parameterized/staticval/someval"
response: static path segments have a high priority
so that a request like curl http://localhost:8001/parameterized/staticval/someval
would
return static path segments have a high priority
.
The method
keyword is used for simple matching to request’s verb:
endpoints:
- path: /
method: PATCH
response: Patched!
- path: /
method: POST
response: Posted!
Any header specified in the endpoint description is required to exist in the request headers. The syntax of static values, parameters, regex match and regex capture groups, is exactly the same with how it works in path matching.
Below is an example configuration which demonstrates the how endpoint alternatives recognized in terms of headers:
services:
- port: 8001
endpoints:
- path: "/alternative"
method: GET
headers:
hdr1: myValue
hdr2: "{{myVar}}"
hdr3: "{{regEx 'prefix-(.+)-suffix' 'myCapturedVar'}}"
response:
body: 'headers match: {{request.headers.hdr1}} {{myVar}} {{myCapturedVar}}'
status: 201
headers:
Set-Cookie:
- name1={{request.headers.hdr2}}
- name2={{request.headers.hdr3}}
- path: "/alternative"
headers:
hdr4: another header
response:
body: 'hdr4 request header: {{request.headers.hdr4}}'
headers:
hdr4: 'hdr4 request header: {{request.headers.hdr4}}'
For the above configuration example here are some example requests and their responses:
curl http://localhost:8001/alternative -H "hdr1: wrongValue"
Response: 404
curl http://localhost:8001/alternative -H "hdr1: myValue" -H "hdr2: someValue"
Response: 404
curl http://localhost:8001/alternative -H "hdr1: myValue" -H "hdr2: someValue" -H "hdr3: prefix-invalidCapture"
Response: 404
curl http://localhost:8001/alternative -H "hdr1: myValue" -H "hdr2: someValue" -H "hdr3: prefix-validCapture-suffix"
Response: 201
with headers match: mvValue someValue validCapture
(also it sets the cookies name1
to someValue
and name2
to validCapture
)curl http://localhost:8001/alternative -H "hdr4: another header"
Response: 200
with hdr4 request header: another header
curl http://localhost:8001/alternative -H "hdr5: another header"
Response: 404
The matching logic for query strings is quite similar to headers. Here is the similar example but in terms of the query strings:
services:
- name: Service1
port: 8001
endpoints:
- path: "/alternative"
method: GET
queryString:
param1: my Value
param2: "{{myVar}}"
param3: "{{regEx 'prefix-(.+)-suffix' 'myCapturedVar'}}"
response:
body: 'query string match: {{request.queryString.param1}} {{myVar}} {{myCapturedVar}}'
status: 201
- path: "/alternative"
queryString:
param4: another query string
response:
body: 'param4 request query string: {{request.queryString.param4}}'
and these are the example requests and corresponding responses for such a mock server configuration:
curl http://localhost:8001/alternative?param1=wrongValue"
Response: 404
curl http://localhost:8001/alternative?param1=my%20Value¶m2=someValue"
Response: 404
curl http://localhost:8001/alternative?param1=my%20Value¶m2=someValue¶m3=prefix-invalidCapture"
Response: 404
curl http://localhost:8001/alternative?param1=my%20Value¶m2=someValue¶m3=prefix-validCapture-suffix"
Response: 201
with query string match: mvValue someValue validCapture
curl http://localhost:8001/alternative?param4=another%20query%20string"
Response: 200
with param4 request query string: another query string
curl http://localhost:8001/alternative?param5=another%20query%20string"
Response: 404
There are several ways of matching request body: with regEx
on text
or with JSON Schema, also you can have criteria
for urlencoded and multipart request bodies.
To match request by urlencoded or multipart form POST, use urlencoded
or multipart
section under body
. The content of that section is very much like query string or headers matching by parameter name:
services:
- port: 8001
endpoints:
- path: "/body-urlencoded"
method: POST
body:
urlencoded:
param1: myValue
param2: "{{myVar}}"
param3: "{{regEx 'prefix-(.+)-suffix' 'myCapturedVar'}}"
- path: "/body-multipart"
method: POST
body:
multipart:
param1: myValue
param2: "{{myVar}}"
param3: "{{regEx 'prefix-(.+)-suffix' 'myCapturedVar'}}"
To match request body text using regEx
, just do it like this:
services:
- name: Mock for Service1
port: 8001
endpoints:
- path: "/endpoint1"
method: POST
body:
text: {{regEx '"jsonkey": "expectedval-(.+)"' 'namedValue'}}
Note: you can use familiar regEx
named value capturing for body, as usual.
To do the match against JSON Schema, please consider this example:
services:
- name: Mock for Service1
port: 8001
endpoints:
- path: "/endpoint1"
method: POST
body:
schema:
type: object
properties:
somekey: { }
required:
- somekey
response: 'endpoint1: body JSON schema matched'
and here is a two request example that one matches the JSON schema while the other doesn’t match:
curl -X POST http://localhost:8001/endpoint1?param1=wrongValue \
-H "Accept: application/json"
-d '{"somekey": "valid"}'
Response: 200
- endpoint1: body JSON schema matched
curl -X POST http://localhost:8001/endpoint1?param1=wrongValue \
-H "Accept: application/json"
-d '{"somekey2": "invalid"}'
Response: 400
If you want to reference JSONSchema from an external file, use it like this:
services:
- name: Mock for Service1
port: 8001
endpoints:
- path: "/endpoint1"
method: POST
body:
schema: "@path/to/schema.json"
The body
field can also be used to match incoming GraphQL requests.
graphql-query
fieldTo do that, the graphql-query
field should be defined under the body
field:
services:
- name: Mock for Service1
port: 8001
endpoints:
- path: "/endpoint1"
method: POST
response:
headers:
Content-Type: application/json
body: '@templates/resp1.json.hbs'
useTemplating: true
body:
graphql-query: |
query HeroNameAndFriends {
hero(
where: {name: {_eq: "{{regEx '(.*)' 'name_eq'}}"}, _and: {age: {_gt: {{regEx '(.*)' 'age_gt'}}}}}
) {
name
age
friends {
name
}
}
}
The graphql-query
contains the template for regex generation. So it translates into the regex below:
query\ HeroNameAndFriends\ \{\
\ \ hero\(\
\ \ \ \ where:\ \{name:\ \{_eq:\ "(.*)"\},\ _and:\ \{age:\ \{_gt:\ (.*)\}\}\}\
\ \ \)\ \{\
\ \ \ \ name\
\ \ \ \ age\
\ \ \ \ friends\ \{\
\ \ \ \ \ \ name\
\ \ \ \ \}\
\ \ \}\
\}
and this regex matches to a GraphQL request like below:
{"query": "query HeroNameAndFriends {\n hero(\n where: {name: {_eq: \"hello\"}, _and: {age: {_gt: 30}}}\n ) {\n name\n age\n friends {\n name\n }\n }\n}\n"}
Such that we can respond with a template like this:
{
"data": {
"hero": {
"name": "{{ name_eq }}",
"age": {{ random.int age_gt 50 }},
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
It’s possible to define variations of a GraphQL query such that you can select the fields by adding or subtracting them from the GrahpQL query matching definition:
graphql-query: |
query HeroNameAndFriends {
hero(
where: {name: {_eq: "{{regEx '(.*)' 'name_eq'}}"}, _and: {age: {_gt: {{regEx '(.*)' 'age_gt'}}}}}
) {
name
age
}
}
and you would respond to a query like this with response template like below:
{
"data": {
"hero": {
"name": "{{ name_eq }}",
"age": {{ random.int age_gt 50 }}
}
}
}
graphql-variables
fieldBesides the graphql-query
field, you can define an additional field named graphql-variables
to
specify matching criteria for GraphQL variables:
services:
- name: Mock for Service1
port: 8001
endpoints:
- path: "/endpoint1"
method: POST
response:
headers:
Content-Type: application/json
body: '@templates/resp1.json.hbs'
useTemplating: true
body:
graphql-query: |
query HeroNameAndFriends {
hero(
where: {name: {_eq: "{{regEx '(.*)' 'name_eq'}}"}, _and: {age: {_gt: {{regEx '(.*)' 'age_gt'}}}}}
) {
name
city
}
}
graphql-variables:
var1: val1
var2: "{{regEx '\\d'}}"
Such that this would match into a request like below:
{
"query": "query HeroNameAndFriends {\n hero(\n where: {name: {_eq: \"hello\"}, _and: {age: {_gt: 30}}}\n ) {\n name\n city\n }\n}\n",
"variables": {
"var1": "val1",
"var2": 3
}
}