Creating Expectations
To create an expectation you need to define:
- the request matcher and
- what response should be returned or
- where the request should be forwarded or
- what callback should be executed.
MockServer will play expectations in the exact order they are setup. For example, if an expectation A is setup to response (or forward) 3 times then expectation B is setup to response (or forward) 2 times for the same request MockServer will response (or forward) in the following order A, A, A, B, B.
Java
To setup an expectation that responds to a request:
new MockServerClient("127.0.0.1", 1080)
.when(
request()
.withMethod("POST")
.withPath("/login")
.withQueryStringParameters(
new Parameter("returnUrl", "/account")
)
.withCookies(
new Cookie("sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW")
)
.withBody(exact("{username: 'foo', password: 'bar'}")),
exactly(1)
)
.respond(
response()
.withStatusCode(401)
.withHeaders(
new Header("Content-Type", "application/json; charset=utf-8"),
new Header("Cache-Control", "public, max-age=86400")
)
.withBody("{ message: 'incorrect username and password combination' }")
.withDelay(new Delay(SECONDS, 1))
);
To setup an expectation that forwards to a request:
new MockServerClient("127.0.0.1", 1080)
.when(
request()
.withMethod("GET")
.withPath("/index.html"),
exactly(1)
)
.forward(
forward()
.withHost("www.mock-server.com")
.withPort(80)
.withScheme(HTTP)
);
To start a local instance of MockServer and setup an expectation that executes a callback:
MockServerClient mockServer = startClientAndServer(1080);
mockServer
.when(
request()
.withPath("/callback")
)
.callback(
callback()
.withCallbackClass("org.mockserver.server.PrecannedTestExpectationCallback")
);
and the callback class could be as follows:
public class PrecannedTestExpectationCallback implements ExpectationCallback {
public static HttpResponse httpResponse = response()
.withStatusCode(ACCEPTED_202.code())
.withHeaders(
header("x-callback", "test_callback_header"),
header("Content-Length", "a_callback_response".getBytes().length),
header("Connection", "keep-alive")
)
.withBody("a_callback_response");
@Override
public HttpResponse handle(HttpRequest httpRequest) {
if (httpRequest.getPath().getValue().endsWith("/callback")) {
return httpResponse;
} else {
return notFoundResponse();
}
}
}
Note: these examples require the use of these static imports as follows:
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import static org.mockserver.model.HttpForward.forward;
import static org.mockserver.model.Header.header;
import static org.mockserver.model.HttpResponse.notFoundResponse;
import static org.mockserver.model.HttpResponse.response;
import static org.mockserver.matchers.Times.exactly;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.mockserver.model.HttpForward.Scheme.HTTP;
import static org.mockserver.model.HttpStatusCode.ACCEPTED_202;
The mockserver-example project contains multiple examples such as BookPageIntegrationTest that demonstrates a fully working examples.
JavaScript
To setup a simple expectation that returns a JSON body for all requests on a given path:
mockServerClient("localhost", 1080).mockSimpleResponse('/somePath', { name: 'value' }, 203);
To setup a more complex expectation:
mockServerClient("localhost", 1080).mockAnyResponse(
{
'httpRequest': {
'method': 'POST',
'path': '/somePath',
'queryStringParameters': [
{
'name': 'test',
'values': [ 'true' ]
}
],
'body': {
'type': 'STRING',
'value': 'someBody'
}
},
'httpResponse': {
'statusCode': 200,
'body': JSON.stringify({ name: 'value' }),
'delay': {
'timeUnit': 'MILLISECONDS',
'value': 250
}
},
'times': {
'remainingTimes': 1,
'unlimited': false
}
}
);
To setup an expectation that forwards all requests:
mockServerClient("localhost", 1080).mockAnyResponse(
{
'httpRequest': {
'method': 'GET',
'path': '/somePath'
},
"httpForward": {
"host": "www.mock-server.com",
"port": 80,
"scheme": "HTTP"
},
'times': {
'remainingTimes': 1,
'unlimited': false
}
}
);
It is also possible to make AJAX calls directly without the client as follows:
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("PUT", "http://localhost:1080/expectation", false);
xmlHttpRequest.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xmlHttpRequest.send(JSON.stringify({
"httpRequest": {
"method": "POST",
"path": "/login",
"body": {
"type": "JSON",
"value": JSON.stringify({ username: "foo", password: "bar" })
}
},
"httpResponse": {
"statusCode": 401,
"headers": [
{
"name": "Content-Type",
"values": ["application/json; charset=utf-8"]
},
{
"name": "Cache-Control",
"values": ["public, max-age=86400"]
}
],
"body": JSON.stringify({ message: "incorrect username and password combination" })
}
}));
Ruby
To setup an expectation that responds to a request:
require "mockserver-client"
class SomeClass
include MockServer
include MockServer::Model::DSL
def createExpectation
client = MockServerClient.new('localhost', 1080)
expectation = expectation do |expectation|
expectation.request do |request|
request.method = 'POST'
request.path = '/login'
request.query_string_parameters << parameter('returnUrl', '/account')
request.cookies = [cookie('sessionId', '2By8LOhBmaW5nZXJwcmludCIlMDAzMW')]
request.body = exact({ username: 'foo', password: 'bar' }.to_json)
end
expectation.response do |response|
response.status_code = 401
response.headers << header('Content-Type', 'application/json; charset=utf-8')
response.headers << header('Cache-Control', 'public, max-age=86400')
response.body = body({ message: 'incorrect username and password combination' }.to_json)
response.delay = delay_by(:SECONDS, 1)
end
end
client.register(expectation)
end
end
To setup an expectation that forwards all requests:
client = MockServerClient.new('localhost', 1080)
expectation = expectation do |expectation|
expectation.request do |request|
request.method = 'GET'
request.path = '/somePath'
end
expectation.forward do |forward|
forward.host = 'www.mock-server.com'
forward.port = 80
forward.scheme = :HTTP
end
expectation.times = exactly(2)
end
client.register(expectation)
Request Matchers
To specify when a response should be mocked a request matcher must be provided in each expectation.
When an incoming request matches the request matcher in an expectation MockServer will return the response for that expectation.
Requests can be matched on:
- method - plain text or regular expression
- path - plain text or regular expression
- query string - plain text or regular expression
- headers - plain text or regular expression
- cookies - plain text or regular expression
- body
- XPath
- XML
- XML Schema
- JSON - supports two match types STRICT and ONLY_MATCHING_FIELDS. STRICT match type matches all fields and the order of arrays. In STRICT match type extra fields will cause the matcher to fail. ONLY_MATCHING_FIELDS match type only matches fields provided in the request matcher.
- JSON Schema
- regular expression
- plain text (i.e. exact match)
- form fields (i.e. body parameters)
- secure - true if the request was made using HTTPS
All matchers for the fields above can also be negated, even the entire request matcher can be negated. When a matched is negated anything except what is specified in the matcher is matched.
For example if a path matcher for "/some/path" is negated by adding a "!" at the start (as follows "!/some/path") then any path except "/some/path" is matched.
Java Request Matcher API
To specify a request matcher in Java use the org.mockserver.model.HttpRequest class which specifies the details of each HTTP response with a fluent API.
For example:
HttpRequest httpRequest =
request()
.withMethod("POST")
.withPath("/login")
.withBody("{username: 'foo', password: 'bar'}")
.withQueryStringParameters(
new Parameter("returnUrl", "/account")
)
.withCookies(
new Cookie("sessionId", "2By8LOhBmaW5nZXJwcmludCIlMDAzMW")
);
JavaScript Request Matcher API
To specify a request matcher in JavaScript use JSON to specify the details with the following format:
"httpRequest": {
"method": "",
"path": "",
"queryStringParameters": []
"body": {
"type": <"STRING" or "REGEX" or "JSON" or "JSON_SCHEMA" or "XPATH" or "PARAMETERS">,
"value": "" ( or "parameters" as shown in more detail below )
},
"cookies": [],
"headers": [],
}
Note: Only values which are matched on need to be specified, if a value is missing then it will not be matched on. For example, below both the parameters and headers fields are not specified.
Cookies, Headers, Parameters
Each cookie, header or parameter array entry has the following syntax:
{
"name": "someName",
"values": ["someValueOne", "someValueTwo", ...]
}
The same example, as in Java above, in JavaScript would be :
"httpRequest": {
"method": "POST",
"path": "/login",
"queryStringParameters": [
{
"name": "returnUrl",
"values": ["/account"]
}
],
"cookies": [
{
"name": "sessionId",
"values": ["2By8LOhBmaW5nZXJwcmludCIlMDAzMW"]
}
],
"body": {
"type": "STRING",
"value": "{username: 'foo', password: 'bar'}"
}
}
Bodies
The "type" value in "body" can be:
"STRING"
"REGEX"
"JSON"
"JSON_SCHEMA"
"XPATH"
"PARAMETERS"
When the "type" field has a value of "STRING", "REGEX", "JSON", "JSON_SCHEMA" or "XPATH" the other field of "body"" should be "value" and it should provide a string to perform the match against, as shown in the example above.
Parameter Bodies
When the "type" field has a value of "PARAMETERS" the other field of "body" should be "parameters" and it should provide a list of body parameters using the following syntax:
{
"name": "someName",
"values": ["someValueOne", "someValueTwo", ...]
}
For example:
"httpRequest": {
"method": "POST",
"path": "/login",
"queryStringParameters": [
{
"name": "returnUrl",
"values": ["/account"]
}
],
"cookies": [
{
"name": "sessionId",
"values": ["2By8LOhBmaW5nZXJwcmludCIlMDAzMW"]
}
],
"body": {
"type": "PARAMETERS",
"parameters": [
{
"name": "username",
"values": ["foo"]
},
{
"name": "password",
"values": ["bar"]
}
]
}
}
String Body Matcher
When the "type" field has a value of "STRING" a short hand can be used where the "body" is only specified as a string literal for example:
"httpRequest": {
"method": "POST",
"path": "/login",
"queryStringParameters": [
{
"name": "returnUrl",
"values": ["/account"]
}
],
"cookies": [
{
"name": "sessionId",
"values": ["2By8LOhBmaW5nZXJwcmludCIlMDAzMW"]
}
],
"body": "a string body showing the short-hand way to specify a simple string body"
}
JSON Object Body Matchers
When the "type" field has a value of "JSON" an additional field can be provided to specify the match type called "matchType".
The JSON expression supports two match types "STRICT" and "ONLY_MATCHING_FIELDS". "STRICT" match type matches all fields and the order of arrays. In "STRICT" match type extra fields will cause the matcher to fail. "ONLY_MATCHING_FIELDS" match type only matches fields provided in the body for the request matcher. "ONLY_MATCHING_FIELDS" match type will match correctly against a request that contains additional fields or a request that contains any array fields those elements are in a different order.
"httpRequest": {
"method": "POST",
"path": "/login",
"body": {
"type": "JSON",
"value": "{username: 'foo', password: 'bar'}",
"matchType": "STRICT"
}
}
JSON Schema Body Matcher
MockServer supports matching request bodies using JSON Schema, which is useful for matching or validating the format of a JSON object. The detailed syntax of JSON Schema is documented at http://json-schema.org.
To use JSON Schema to match a request body the "type" field should have a value of "JSON_SCHEMA" and the "value" field should contain the JSON Schema as a string value, as follows:
"httpRequest": {
"method": "POST",
"path": "/login",
"body": {
"type": "JSON_SCHEMA",
"value": "{
"type": "object",
"properties": {
"username": {
"type": "string",
"pattern": "^[a-z0-9_-]{3,15}$"
},
"password": {
"type": "string",
"minLength": 8
}
},
"required": [
"username",
"password"
]
}"
}
}
Response Actions (Mock Responses)
Response actions contain:
- status code i.e. 200, 302, 404, etc
- body - a UTF-8 encoded sequence of bytes containing any content
- headers - each with a name and one or more values
- cookies - each with a name and with one or more values, more complex cookies can be modelled by using the a Set-Cookie header
Responses can be further controlled using:
- a delay before the response is sent
- the number of times the response is sent (including unlimited)
- a time to live the response will be continued to be returned (including unlimited)
Java
When mocking a response in Java use the org.mockserver.model.HttpResponse class which specifies the details of each HTTP response with a fluent API, for example:
HttpResponse httpResponse =
response()
.withStatusCode(401)
.withHeaders(
new Header("Content-Type", "application/json; charset=utf-8"),
new Header("Cache-Control", "public, max-age=86400")
)
.withBody("{ message: 'incorrect username and password combination' }")
.withDelay(new Delay(TimeUnit.SECONDS, 1))
.withConnectionOptions(
new ConnectionOptions()
.withKeepAliveOverride(true)
.withCloseSocket(true)
);
To control whether the socket is closed after the response has been sent or headers such as "Connection" or "Content-Length" the org.mockserver.model.ConnectionOptions class can be used.
For example:
HttpResponse httpResponse =
response()
.withStatusCode(401)
.withHeaders(
new Header("Content-Type", "application/json; charset=utf-8"),
new Header("Cache-Control", "public, max-age=86400")
)
.withBody("{ message: 'incorrect username and password combination' }")
.withDelay(new Delay(TimeUnit.SECONDS, 1))
.withConnectionOptions(
new ConnectionOptions()
.withKeepAliveOverride(true)
.withCloseSocket(true)
);
JavaScript
To mock a response in javascript use JSON to specify the details with the following format:
"httpResponse": {
"statusCode": 200,
"body": "",
"cookies": [],
"headers": [],
"delay": {
"timeUnit": "MICROSECONDS",
"value": 0
}
}
Each cookie or header array entry has the following syntax:
{
"name": "someName",
"values": ["someValueOne", "someValueTwo", ...]
}
The "timeUnit" value in "delay" can be:
"NANOSECONDS"
"MICROSECONDS"
"MILLISECONDS"
"SECONDS"
"MINUTES"
"HOURS"
"DAYS"
The same example as above would be:
"httpResponse": {
"statusCode": 401,
"headers": [
{
"name": "Content-Type",
"values": ["application/json; charset=utf-8"]
},
{
"name": "Cache-Control",
"values": ["public, max-age=86400"]
}
],
"body": JSON.stringify({ message: "incorrect username and password combination" }),
"delay": {
"timeUnit": "SECONDS",
"value": 1
}
}
Forward Actions
Forward actions contain:
- host - the host to forward to i.e. www.mock-server.com
- port - the port to forward to, this defaults to 80 if not specified
- scheme - the scheme to use, either HTTP or HTTPS, this defaults to HTTP if not specified
Forward actions can be further controlled using:
- the number of times the request is forwarded (including unlimited)
- a time to live the response will be continued to be returned (including unlimited)
Java
When mocking a forward in Java use the org.mockserver.model.HttpForward class which specifies the details of each HTTP(S) forward with a fluent API, for example:
HttpForward httpForward =
forward()
.withHost("www.mock-server.com")
.withPort(80)
.withScheme(HttpForward.Scheme.HTTP);
The full specification of org.mockserver.model.HttpForward is as follows:
public class HttpForward {
/**
* The host or ip address to forward the request to i.e. "www.mock-server.com"
*
* @param host a hostname or ip address as a string
*/
public HttpForward withHost(String host);
/**
* The port to forward the request to i.e. 80. If not specified the port defaults to 80.
*
* @param port a port as an integer
*/
public HttpForward withPort(Integer port);
/**
* The scheme to use when forwarded the request, either HTTP or HTTPS. If not specified the scheme defaults to HTTP.
*
* @param scheme the scheme as a HttpForward.Scheme value
*/
public HttpForward withScheme(Scheme scheme);
}
JavaScript
To mock a response in javascript use JSON to specify the details with the following format:
"httpForward": {
"host": "",
"port": 80,
"scheme": "HTTP"
}
The "scheme" value in can be:
"HTTP"
"HTTPS"
The same example as above would be:
"httpForward": {
"host": "www.mock-server.com",
"port": 80,
"scheme": "HTTP"
}
Callback Actions
A callback specifies a class that will be called for each matching request and can be used to dynamically generate the response.
Callback actions contain:
- callbackClass
The callback class must:
- have a default constructor
- implement org.mockserver.mock.action.ExpectationCallback
- be available on the classpath (see below)
Callback actions can be further controlled using:
- the number of times the callback is called (including unlimited)
- a time to live the callback will be continued to be called (including unlimited)
If MockServer is started using the JUnitRule org.mockserver.junit.MockServerRule or using org.mockserver.integration.ClientAndServer or directly using the org.mockserver.mockserver.MockServerBuilder then any class present in the main or test classpaths will be visible to MockServer.
If MockServer is started using the plugin only the non-forked goals (such as runAndWait and start) will be able to load classes from the main and test classpaths. It is possible to use classes from a separate maven dependency, however, this dependency must be specified in the plugin configuration dependencies section. Any dependency added to the plugin configuration dependencies section will then be visible to MockServer run using both forked and non-forked goals.
The following configuration shows how to use classes from a separate maven dependency in callback actions.
<plugin>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-maven-plugin</artifactId>
<version>4.0.0</version>
<configuration>
<serverPort>1080</serverPort>
<logLevel>DEBUG</logLevel>
<pipeLogToConsole>true</pipeLogToConsole>
</configuration>
<executions>
<execution>
<id>pre-integration-test</id>
<phase>pre-integration-test</phase>
<goals>
<goal>runForked</goal>
</goals>
</execution>
<execution>
<id>post-integration-test</id>
<phase>post-integration-test</phase>
<goals>
<goal>stopForked</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.my-domain</groupId>
<artifactId>my-callback-dependency</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</plugin>
If MockServer is started using the command line then the callback classes must be added to the JVM using the classpath command line switch (cp or classpath). The classpath switch is ignored by the JVM if the jar switch is used. So to run the MockServer from the command line directly (with mockserver-netty-4.0.0-jar-with-dependencies.jar) you must specify the org.mockserver.cli.Main main class specifically and not use the jar switch as follows.
java -Dfile.encoding=UTF-8 -cp mockserver-netty-4.0.0-jar-with-dependencies.jar:my-callback-dependency.jar org.mockserver.cli.Main -serverPort 1080
Java
When mocking a callback in Java use the org.mockserver.model.HttpClassCallback class which specifies the details class to callback as follows, for example:
HttpCallback httpClassCallback = callback("org.some.package.MyCallback");
The full specification of org.mockserver.model.HttpClassCallback is as follows:
public class HttpCallback {
/**
* The class to callback as a fully qualified class name
*
* This calls must:
* - implement org.mockserver.mock.action.ExpectationCallback
* - have a zero argument constructor
* - be available in the classpath of the MockServer
*
* @param callbackClass class to callback as a fully qualified class name, i.e. "com.foo.MyExpectationCallback"
*/
public HttpCallback withCallbackClass(String callbackClass);
}
The class specified must implement the org.mockserver.mock.action.ExpectationCallback interface as follows:
public interface ExpectationCallback {
/**
* Called for every request when expectation condition has been satisfied.
* The request that satisfied the expectation condition is passed as the
* parameter and the return value is the response that will be returned to the client.
*
* @param httpRequest the request that satisfied the expectation condition
* @return the response that will be returned to the client
*/
public HttpResponse handle(HttpRequest httpRequest);
}
Times
The org.mockserver.matchers.Times class is used to specify how many times you want MockServer to match a request:
To create an instance of Times use one of the static factory methods:
Times.unlimited();
Times.once();
Times.exactly(int count);
JavaScript
To setup a callback in javascript use JSON to specify the details with the following format:
"httpClassCallback": {
"callbackClass": "org.mypackage.MyCallbackClass"
}