Dynamic Mock
Dynamic Mock is the most flexible way of defining mocks. You write JavaScript code that sets HTTP response fields (status, headers, body) to values required by your use case. SmartMock.io supports most features of the ES2015 specification. Besides standard JavaScript, SmartMock.io runtime provides a bunch of utility libraries that help you to create more concise and smarter code. Code submitted by you is executed safely, and SmartMock.io separates client code executions from each other.
Code Guide and API Documentation
There are four global objects available in the code:
req- an object which represents an HTTP request sent to SmartMock.iores- an object which represents an HTTP response sent back by SmartMock.ioconsole- logger, which may be used for debugging purposes. Its output is visible in the Request log.state- a JavaScript object, which may be modified by code. It holds user-defined data.
Request (req)
The HTTP request the client sends to SmartMock.io is parsed and supplied to a matched Dynamic Mock. The HTTP request is represented by
a req object, which is a global variable available in any part of code (unless shadowed)
Fields
The following fields are available in the req object:
-
req.method- Value of request HTTP method -
req.url- The URL, the client, used to make the request. The returned URL contains a protocol, server name, port number, and server path, but it does not include query string parameters.
Example
Request URL:
http://somedomain.app.smartmock.io/user/John%20Doe/permissions?param1=value1
Code:
res.setBody(req.url);
Rendered response body:
http://somedomain.app.smartmock.io/user/John%20Doe/permissions
req.path- The path part of the request's URL. It does not decode the path.
Example
Request URL:
http://somedomain.app.smartmock.io/user/John%20Doe/permissions?param1=value1
Code:
res.setBody(req.path);
Rendered response body:
/user/John%20Doe/permissions
req.pathParams- Structure (map) containing mapping between the name of the path parameter and its value extracted from the request path (see Path Parameter Matchers). If the parameter is not found,undefinedvalue is returned.
Example
Path matcher:
/user/{userId}/permissions
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?param1=value1
Code:
res.setBody(req.pathParams['userId']);
Rendered response body:
123abc
req.pathSegments- An array containing request path segments (tokenized by/character). Accessing a non-existent segment returnsundefinedvalue.
Example
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?param1=value1
Code:
res.setBody(req.pathSegments[1])
Rendered response body:
123abc
req.host- The name of the host request was made to
Example
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?param1=value1
Code:
res.setBody(req.host);
Rendered response body:
somedomain.app.smartmock.io
req.headers- Structure containing information about request headers. This is a simple mapping of the header name to an array of values. If accessing a non-existent header, undefined value is returned. We recommend using methods access to access headers.
Example
Request Headers:
Content-Type: application/json
AuthToken: abc123
AuthToken: bcd123
Code:
res.setBody(req.headers['AuthToken'][1]);
Rendered response body:
bcd123
req.cookies- Structure containing information about request cookies. This is a simple mapping of a cookie name to an array of values. If accessing non-existent cookie, undefined value is returned. We recommend using methods access to access cookies.
Example
Request Headers:
Cookie: PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;
Code:
res.setBody(req.cookies['PHPSESSID'][0]);
Rendered response body:
298zf09hf012fh2
req.query- Structure containing information about request query parameters. This is a simple mapping of the query parameter name to an array of values. If accessing non-existent query parameters, undefined value is returned. We recommend using methods access to access query parameters.
Example
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?page=1&size=10
Code:
res.setBody(req.query['page'][0]);
Rendered response body:
1
req.queryString- Value of request URL query string. If the request URL has no query string, the empty string is used.
Example
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?page=1&size=10
Code:
res.setBody(req.queryString);
Rendered response body:
page=1&size=10
req.body- Value of raw request body. It is always a string value. If you need a parsed body, please usejsonBodyproperty.
Example
Request body:
{"id" : 123, "name": "John", "surname" : "Doe"}
Code:
res.setBody(req.body);
Rendered response body:
{"id" : 123, "name": "John", "surname" : "Doe"}
req.jsonBody- Request body object parsed from raw body JSON. If raw body is not valid JSON string, aSyntaxErroris thrown.
Example
Request body:
{"id" : 123, "name": "John", "surname" : "Doe"}
Code:
res.setBody(req.jsonBody.id);
Rendered response body:
123
Request Methods
req.getHeader(name)- Returns first value of header with givennameorundefinedwhen header not found. Parameternameis case-insensitive.
Example
Request Headers:
Content-Type: application/json
AuthToken: abc123
Code:
res.setBody(req.getHeader('AuthToken'));
Rendered response body:
abc123
-
req.hasHeader(name)- Returnstrueif header with givennameexists in request orfalseotherwise. Parameternameis case-insensitive. -
req.getHeaders(name)- Returns an array of values of the header with givennameorundefinedwhen the header not found. Parameternameis case-insensitive. -
req.getCookie(name)- Returns first value of cookie with givennameorundefinedwhen cookie not found. Parameternameis case sensitive.
Example
Request Headers:
Cookie: PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;
Code:
res.setBody(req.getcookie('PHPSESSID'));
Rendered response body:
298zf09hf012fh2
req.hasCookie(name)- Returnstrueif cookie with givennameexists in request orfalseotherwise. Parameternameis case sensitive.-
req.getCookiess(name)- Returns an array of values of cookie with givennameorundefinedwhen cookie not found. Parameternameis case sensitive. -
req.getPathParam(name)- Returns value of path parameter with givennameorundefinedwhen path parameter not found. Parameternameis case sensitive.
Example
Path matcher:
/user/{userId}/permissions
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?param1=value1
Code:
res.setBody(req.getPathParam('userId'));
Rendered response body:
123abc
-
req.hasPathParam(name)- Returnstrueif path parameter with givennameexists in request orfalseotherwise. Parameternameis case sensitive. -
req.getQueryParam(name)- Returns first value of query parameter with givennameorundefinedwhen query parameter not found. Parameternameis case sensitive.Example
Request URL:
http://somedomain.app.smartmock.io/user/123abc/permissions?page=1&size=10Code:
res.setBody(req.getQueryParam('page'));Rendered response body:
1 -
req.hasQueryParam(name)- Returnstrueif query parameter with givennameexists in request orfalseotherwise. Parameternameis case sensitive. req.getQueryParams(name)- Returns an array of values of query parameter with givennameorundefinedwhen query parameter not found. Parameternameis case sensitive.req.hasJsonBody()- Returnstrueif request body contains parseable JSON value orfalseotherwise.
Response (res)
The HTTP response sent back to the client is represented by the res object. It exposes the API to set HTTP status, headers, and body.
By default, it returns status 200 without headers and an empty body.
Fields
res.status- Sets status of response. Accepts integral values from 100 to 599. ThrowsTypeErrorwhen trying to set up an invalid status code.res.body- Sets raw body of response. ThrowsTypeErrorif body is an object containing circular references.
Example
Code:
let person = {name: 'John', surname: 'Doe'};res.body = person;Rendered response headers:
{"name": "John", "surname": "Doe"}Methods
res.setStatus(status)- Sets status of response. Accepts integral values from 100 to 599. ThrowsTypeErrorwhen trying to set up invalid status code.res.addHeader(name, value)- Adds response header with givennameandvalue.valuemay be a string or array of strings. ThrowsTypeErrorifvaluecontains circular references.
Example
Code:
res.addHeader('Token', 'abc123');res.addHeader('Token', ['abc123', 'bcd234']);Rendered response headers:
Token: abc123Token: abc123Token: bcd234res.setHeader(name, value)- Sets response header with givennameandvaluereplacing already existing headers.valuemay be a string or array of strings. ThrowsTypeErrorifvaluecontains circular references.
Example
Code:
res.addHeaders({'Content-Type': 'application/json', 'Page-Size': '10'});Rendered response headers:
Content-Type: text/plainPage-Size: 10res.addHeaders(headers)- Adds multiple headers in a single call.headersparameter must be a plain JavaScript object.
Example
Code:
res.addHeader('Content-Type', 'application/json');res.setHeaders({'Content-Type': 'application/json', 'Page-Size': '10'});Rendered response headers:
Content-Type: text/plainPage-Size: 10res.setHeaders(headers)- Sets multiple headers in a single call replacing already existing headers.headersparameter must be a plain JavaScript object.
Example
Code:
res.addHeader('Content-Type', 'application/json');res.setHeader('Content-Type', 'text/plain');Rendered response headers:
Content-Type: text/plainres.getHeader(name)- Returns an array of values for header with givennameorundefinedwhen header not found.-
res.hasHeader(name)- Returnstrueif header with givennameexists in request orfalseotherwise. -
res.setBody(body)- Sets body of response.bodyparameter may be any JavaScript type. SmartMock.io runtime will convert it to a string. ThrowsTypeErrorif body is an object containing circular references,
Example
Code:
let person = {name: 'John', surname: 'Doe'};res.setBody(person);Rendered response headers:
{"name": "John", "surname": "Doe"}Console (console)
The Console is a logger which may be used for debugging purposes. Its output is visible in the Request log.
Limits
Only the first 10000 characters of the console log is available in the Request log. It should be enough to troubleshoot most of the problems.
Methods
console.log(message) - Appends a diagnostic message useful for troubleshooting.
Example
Code:
conole.log('Header value is:', req.getHeader('Content-Type'));Log generated:
2018-05-21T12:17:01.426Z - Header value is: application/jsonBuilt-in Libraries
Besides an API, which gives access to the HTTP request, response, state, and console, SmartMock.io also provides some bundled libraries you may use in your code. All listed libraries are already imported and are available through global variables. The following table presents a summary of available libraries. There are more details and examples below the table.
| Library | Version | Variable name |
|---|---|---|
| lodash | 4.17.11 | _ |
| faker | 4.1.0 | faker |
| moment.js | 2.23.0 | moment |
| qs | 6.6.0 | qs |
| validator | 10.9.0 | validator |
| xmlbuilder | 10.1.1 | xmlbuilder |
| xmldom | 0.1.27 | DOMParser |
| xpath.js | 1.1.0 | select |
| entities | 1.1.2 | entities |
lodash (4.17.11)
Lodash is a modern JavaScript utility library delivering modularity, performance, and extras.
It's available as a _ global variable.
Example: Use lodash utility functions to sort a user array stored in the State.
state.set('users', [
{ user: 'fred', age: 48 },
{ user: 'barney', age: 36 },
{ user: 'fred', age: 40 },
{ user: 'barney', age: 34 }
]);
res.setBody(_.sortBy(state.get('users'), ['age']));
Rendered response body:
[{"user":"barney","age":34},{"user":"barney","age":36},{"user":"fred","age":40},{"user":"fred","age":48}]
faker (4.1.0)
Faker - Library used for generating massive amounts of realistic fake data.
It's available under faker global variable.
Example: Use Faker random generators to generate random person data.
faker.locale = 'en_US';
const person = {
id: faker.random.uuid(),
name: faker.name.firstName(),
surname: faker.name.lastName(),
zipCode: faker.address.zipCode()
};
res.setBody(person);
Rendered response body:
{"id":"965533af-b0ef-426f-b950-95b595ae638f","name":"Jaden","surname":"Klein","zipCode":"31715-6123"}
See Faker Documentation for more usage examples and API Documentation.
moment.js (2.23.0)
Moment.js is a library used for parsing, validating, manipulating, and displaying dates and times in JavaScript.
It's available under moment global variable.
Example: Use the moment library to parse the date passed in the request, subtract one day, and format a new date in the given format.
Request body:
{"dueDate" : "2018-12-14"}
Code:
const dayBefore = moment(req.jsonBody.dueDate, "YYYY-MM-DD").subtract(1, 'day');
res.setBody(dayBefore.format("YYYY-MM-DD"));
Rendered response body:
2018-12-13
See Moment.js Documentation for more usage examples and API Documentation.
qs (6.6.0)
qs is a query string parsing and stringifying library with some added security.
It's available under qs global variable.
Example: Use QS to parse and extract the array of user tags from the request's query string.
Request query string:
id=1111&name=John&tags[]=developer&tags[]=javascript
Code:
var params = qs.parse(req.queryString);
const person = {
id: params.id,
name: params.name,
tags: params.tags
};
res.setBody(person);
Rendered response body:
{"id":"1111","name":"John","tags":["developer","javascript"]}
See QS Documentation for more usage examples and API Documentation.
validator (10.9.0)
validator - A library of string validators and sanitizers.
It's available under validator global variable.
Example: Check if the value passed in request body is a valid email address.
Request body:
{
"name" : "John",
"surname" : "Doe",
"email" : "jj@@doe.com"
}
Code:
const isValid = validator.isEmail(req.jsonBody.email);
if (!isValid) {
res.setHeader('Content-Type', 'application/json');
res.setStatus(400);
res.setBody({error: "Invalid email"});
} else {
res.setStatus(200);
}
Rendered response:
HTTP/1.1 400
Content-Type: application/json
{"error":"Invalid email"}
See Validator Documentation for more usage examples and API Documentation.
xmlbuilder (10.1.1)
xmlbuilder - A utility library that allows simple XML documents to be constructed using relatively sparse JavaScript code.
It's available under xmlbuilder global variable.
Example: Construct an XML response body with fluent JavaScript API.
Code:
var xml = xmlbuilder.create('root')
.ele('xmlbuilder')
.ele('repo', {'type': 'git'}, 'git://github.com/oozcitak/xmlbuilder-js.git')
.end({ pretty: true});
res.setBody(xml);
Rendered response body:
<?xml version="1.0"?>
<root>
<xmlbuilder>
<repo type="git">git://github.com/oozcitak/xmlbuilder-js.git</repo>
</xmlbuilder>
</root>
See XML Builder Documentation for more usage examples and API Documentation.
xmldom (0.1.27) and xpath.js (1.1.0)
xmldom - A JavaScript implementation of W3C DOM for Node.js, Rhino, and the browser. Fully compatible with W3C DOM level2 and some compatibility with level3. Supports DOMParser and XMLSerializer interface, such as in the browser. xpath.js - An XPath module for Node, written in pure javascript.
They are available under DOMParser and select global variable.
Example:
Parse and select id value from a request XML document.
Request body:
<root>
<person>
<id>8dabf158-49c4-44cd-b82c-858642f21553</id>
<name>John</name>
<surname>John</surname>
</person>
</root>
Code:
var doc = new DOMParser().parseFromString(req.body);
var name = select(doc, "//root/person/id/text()")[0].data;
res.setBody(name);
Rendered response body:
8dabf158-49c4-44cd-b82c-858642f21553
entities (1.1.2)
entities - En- & decoder for XML/HTML entities.
It's available under entities global variable.
Example: Unescape XML document sent in a request XML document.
Request body:
<root>
<person>
<id>8dabf158-49c4-44cd-b82c-858642f21553</id>
<name>John</name>
<surname>John</surname>
<metadata>
<tags><tag>developer</tag><tag>javascript</tag></tags>
</metadata>
</person>
</root>
Code:
var doc = new DOMParser().parseFromString(req.body);
var escapedMetadata = select(doc, "//root/person/metadata/text()")[0].data;
const unescapedMetadata = entities.decodeXML(escapedMetadata);
var tagsDoc = new DOMParser().parseFromString(unescapedMetadata);
var tagsList = select(tagsDoc, "//tags/tag/text()");
var body = tags.map((tag) => tag.data);
res.setBody(body);
Rendered response body:
["developer","javascript"]
See Entities Documentation for more usage examples and API Documentation.
Stateful Responses
In some use cases, to model real system behavior, it's necessary to maintain a server-side state between various mocks executions.
SmartMock.io addresses this kind of use case with a global State (state) variable.
State acts like a cache, which holds user-defined data and is shared between mocks in the same workspace.
- State is propagated to code with a global variable
stateand exposes a set of methods (see below) which allow access to State data or to mutate the State. - State is workspace-level object, i.e. all mocks inside a single workspace share it.
- Direct assignment to State is ignored, i.e. does not change the State value (for example:
state = 'aaaaa'- this does not work. Use State methods instead.) - Objects set to the State must be serializable to a JSON string, i.e. cannot contain cyclic references.
- Non-data values set to the state (like functions) are ignored.
- If request ends with an error (for example, due to incorrect code syntax) changes made to the State up to the error line are not propagated, i.e. the workspace State does not change.
State Limits
- State may not contain more than 1000 distinct keys. An error is raised when trying to set more than 1000 distinct keys in the State.
- All values set to the State should not be larger than 100KB after serialization to JSON. If the serialized state is larger than 100KB, an error is raised.
State Methods
state.put(key, value, ttlSec = 604800)- Associatesvaluewithkeyin the State. Thevaluewill be kept in the State up tottlSecseconds and will later expire. The default value ofttlSecis 24 hours (86400 seconds). If the State previously contained a value associated with thekey, the old value is replaced byvalueand its ttl is refreshed. We recommend usingstate.getOrSet(key, value, ttlSec)when using the conventional "if cached, return; otherwise create, cache and return" pattern. Thekeyparameter must be non-empty string. ThettlSecparameter must be a positive integer. IThe default value is 24 hours (3600 *24 = 86400 seconds).
Sets an array of people into the state with TTL of 1 hour Sets an array of people into the state with default TTL of 24 hoursExamples
state.set('people', [{id: 1, name: 'John', surname: 'Doe'}], 3600);
state.set('people', [{id: 1, name: 'John', surname: 'Doe'}]);
state.getOrSet(key, value, ttlSec = 604800)- Returns the value associated with thekeyin the State. If the State does not contain any data for a givenkey, the providedvalueis set into the State and returned. This method improves upon the conventional "if cached, return; otherwise create, cache and return" pattern. Thevaluewill be kept in the State up tottlSecseconds and will later expire. The default value of thettlSecparameter is 24 hours i.e. 86400 seconds. Thekeyparameter must be a non-empty string. ThettlSecparameter must be a positive integer, and its default value is 24 hours (3600 *24 = 86400 seconds).
Returns value from the state for key Resturns value from the state for key Examples
people setting (with TTL of 1 hour) and returning an empty array when state does not contain value for key people.state.getOrSet('people', [], 3600);
people setting (with TTL of 24 hours) and returning an empty array when state does not contain value for key people.state.getOrSet('people', []);
state.get(key)- Returns the value associated with thekeyin the State, orundefinedif there is no value for thekey. Thekeyparameter must be a non-empty string.
Returns value from the state for key Example
people.const people = state.get('people');
-
state.clear()- Discards all entries in the State. -
state.delete(key)- Discards any value forkeyin the State. Thekeyparameter must be a non-empty string.
Deletes value for key Example
people.state.delete('people');
The following state errors may occur:
- State too large (after serialization to JSON state is larger than 100KB) - mock returns an error
- State cannot be serialized to JSON (contains cyclic references) - mock returns an error
Error Handling
Since Dynamic Mock evaluates your code, there may be some problems during the execution of that code:
- Invalid code syntax and accessing
undefinedvariables - Incorrect usage of state or built-in libraries
- Too long a time spent for execution (your code has 2 seconds to evaluate)
- Internal errors caused by a too high memory footprint
For most of the cases described above, mock returns an HTTP 500 status with a generic error message. However, more helpful messages can be found in then Request Logs.