genuine-mock-server
v1.2.9
Published
A real fake mock server for http requests
Downloads
47
Readme
Genuine Mock Server
Table of Contents
- An Example Repo
- Getting Started
- Overview of Mock Files
- Overview of Initizalation Script
- Building the mock server using slurp mode
- Adding Paths to Mocks
- Adding Regex and String Pattern Paths to your Mocks
- How, Query, Param and Body work in mock files
- Subset and Structural Matching with JSON Schema
- Gotchas And FAQ
Getting Started
Create a script to start your mock server at the git root of your project
vim server.js
// Inside server.js const { init } = require('genuine-mock-server'); const mocks = [ { request: { method: 'get', path: '/api/helloworld/example', }, response: { data: { 'key': 'Hello World!', } }, }, ]; init({ port: 8080, mocks: mocks, }
Use your prefered script watcher (We recommend nodemon)
nodemon server.js
or
node server.js
Curl that bad-boy!
curl 'http://localhost:8080/api/helloworld/example'
Building the Mock Server Using Slurp Mode
There is also a second way to build the mock server, and that is using 'slurp' mode. You specify the path to the folders containing mock files, and a file-ending to slurp up into the mock server. Slurp mode is useful if you want a file-to-endpoint naming convention for storing your mocks.
Create a simple mock files directory
mkdir mocks
Create a simple mock file inside
mocks/
vim mocks/example.js
Note: folder structure, file names, etc DO NOT MATTER. Files are slurped recursively. A file named Foobar could map to any endpoint. Naming conventions are entirely up to you!
module.exports = [ { request: { method: 'get', path: '/api/helloworld/example', }, response: { data: { 'key': 'Hello World!', } }, }, ];
Create a script to start your mock server
vim server.js
const { init } = require('genuine-mock-server'); init({ port: 8080, pathToFiles: './mocks', filePattern: '*.js', // whatever file extension you want to target });
Use your prefered script watcher (We recommend nodemon)
nodemon server.js
or
node server.js
Curl that bad-boy!
curl http://localhost:8080/api/helloworld/example
Overview of Mock Files
module.exports = [
{
request: {
// ...
},
response: {
// ...
},
},
]
Request Blob
| Key | Type | Description | Required | |-------- |--------|----------------------------------------------------- |---------------------------- | | path | String | The api endpoint path (not including querystring) | required | | method | String | The http method | optional (defaults to GET) | | params | Object | An object of key / value pairs for path params | optional (defaults to {}) | | body | Object | An object of key / value pairs for the body request | optional (defaults to {}) | | query | Object | An object of key / value pairs for the querystring | optional (defaults to {}) | | matchType | String | A string to select the match algorithm (see JSON Schema) | optional (defaults to 'exact') |
Response Blob
| Key | Type | Description | Required | |------------|---------|---------------------------------------------------------------------|---------------------------------------------| | waitTime | Integer | The time in milliseconds the mockserver will wait before responding | optional (defaults to 0) | | statusCode | Integer | The http status code in the response | optional (defaults to 200) | | data | Object | The data that will be returned in the response from the mock server | optional (by why would you leave it blank?) |
Overview of initialization script
const { init } = require('genuine-mock-server');
init({
port: 8080,
pathToFiles: './mockServer/Mocks',
filePattern: '*.js', // whatever file extension you want to target
mocks: [
{
// ... stuff here
}
]
});
| Key | Type | Description | Required | |-------------|---------|-----------------------------------------------------------------------------------------------|--------------------------------------------------| | port | Integer | The port number for the mock server | required | | pathToFiles | String | The path to the top-level folder containing mock files | required (only if 'mocks' is not included) | | filePattern | String | The file pattern / file extension to be slurped up by the mock server | optional | | mocks | Array | An array of supplied mock objects. Useful if you want to supply programatically created mocks | required (only if 'pathToFiles' is not included) |
Using mocks, slurped mocks, or both
You can use both mock files defined inside a mock folder, or programatically added mock files, or both!
// only slurp mocks are added, since the other keys have been ommitted
const { init } = require('genuine-mock-server');
init({
port: 8080,
pathToFiles: './mocks',
filePattern: '*.js'
});
// only provided mocks are added, since the other keys have been ommited
const { init } = require('genuine-mock-server');
const mocks = [
{
request: {
method: 'get',
path: '/api/helloworld/simple',
},
response: {
data: {
'key': 'Hello World!',
}
},
},
];
init({
port: 8080,
mocks: mocks,
});
// Here, both provided mocks, AND slurped mocks are used
const { init } = require('genuine-mock-server');
const mocks = [
{
request: {
method: 'get',
path: '/api/helloworld/simple',
},
response: {
data: {
'key': 'Hello World!',
}
},
},
];
init({
port: 8080,
mocks: mocks,
pathToFiles: './mockServer/Mocks',
filePattern: '*.js', // whatever file extension you want to target
});
Note: Whichever method you choose is up to you. Provided mocks are added first, then slurped mocks.
Adding Paths to Mocks
One Array of Mocks for Different Endpoints
If you want, you can simply write out different paths by hand in each mock blob. The ability to specify different paths on a per-mock basis is useful if you're building out mocks programatically, and want complete control.
module.exports = [
{
request: {
method: 'get',
path: '/api/helloworld/firstpath',
},
response: {
data: {
'key': 'Hello World!',
}
},
}
{
request: {
method: 'get',
path: '/api/helloworld/secondpath',
},
response: {
data: {
'key': 'Hello World!',
}
},
}
];
One Array of Mocks for the Same Endpoint
Writing out the same path over and over again is error prone, so a helper method is included to make things easier, should you so desire.
const { defaultPath } = require('genuine-mock-server');
module.exports = defaultPath('/api/helloworld/defaultpath/', [
{
request: {
// default path gets added automagically
method: 'get',
},
response: {
data: {
'key': 'I use the default path',
}
},
},
{
request: {
// default path gets added automagically
method: 'delete',
},
response: {
data: {
'key': 'I use the default path as well!',
}
},
},
]);
Note: This method is mostly just a simple reduce function, but it won't clobber any paths you HAVE defined. See below for an example
An Array of Mocks, Mixed Endpoints
You can, if you so desire, add the same path to all mock files, except for a few of them.
const { defaultPath } = require('genuine-mock-server');
module.exports = defaultPath('/api/helloworld/defaultpath/', [
{
request: {
// default path gets added automagically
method: 'get',
},
response: {
data: {
'key': 'I use the default path',
}
},
},
{
request: {
// default path gets added automagically
method: 'delete',
},
response: {
data: {
'key': 'I use the default path as well!',
}
},
},
{
request: {
path: '/api/helloworld/notdefaultpath' // Since the path is defined, it is NOT overriden
method: 'delete',
},
response: {
data: {
'key': 'My path was defined, so I wont be overriden',
}
},
},
]);
Adding Regex and String Pattern Paths to your Mocks
Under the hood, genuine-mock-server
uses express
, so regex paths are standardized
and simple.
// Adding a regex as your path:
module.exports = [
{
request: {
path: new RegExp(/api/g),
.../
},
response: {
// ...
},
},
]
// gets translated under the hood to
app.get(yourRegexHere, () => {
// your mock data gets returned
})
string-patterns
like '/ab(cd)?e'
are also supported (as they are part of the express)
routing. Consult the express
docs for more information: link
Note: Same as any other routing framework, be mindfull of how your regex paths are intercepting.
Like express
, catchall routes will intercept instead of other already-defined paths.
This may, or may not, be what you intended. If in doubt, register your regex paths later,
after the other mocks.
How Query Param and Body work in mock files
module.exports = [
{
request: {
query: {
// ...
},
body: {
// ...
},
params: {
// ...
}
},
response: {
// ...
},
},
]
Genuine Mock server follows a simple strategy for mocking files. Request
represents
any given http request sent to the server, with a given set of paramaters. Any request
that exactly matches these paramaters will return the data supplied in response
.
For more information on how exact matching works, view: How exact matches work
Query Example
Lets say you wanted to mock any GET request to /api/helloworld/people?name=jimmy
.
module.exports = [
{
request: {
path: '/api/helloworld/people'
query: {
name: 'jimmy', // A GET request to '/api/helloworld/people?name=jimmy', would match this request
},
},
response: {
data: {
someKey: "I am the get querystring example response!"
}
},
},
]
// Example semi-psuedocode axios
axios.get('/api/helloworld/people?name=jimmy');
// Example response
{
someKey: "I am the get querystring example response!"
}
Path Example
Lets say you wanted to mock any POST request to /api/helloworld/person/:name
,
where the :name
is equal to 'jimmy'
module.exports = [
{
request: {
path: '/api/helloworld/person/:name'
path: {
name: 'jimmy', // A request to '/api/person/jimmy' would match this request!
},
},
response: {
data: {
someKey: "I am the path param example response!"
}
},
},
]
// Example semi-psuedocode axios
axios.post('/api/helloworld/person/jimmy');
// Example response
{
someKey: "I am the path param example response!"
}
Body Example
Lets say you wanted to mock a POST request to /api/helloworld/people
,
with a json body of { name: 'jimmy' }
module.exports = [
{
request: {
path: '/api/helloworld/person'
body: {
name: 'jimmy', // A POST request to the above path with the given body would match this request!
},
},
response: {
data: {
someKey: "I am the Body example response!"
}
},
},
]
// Example semi-psuedocode axios
axios.post('/api/helloworld/person', {
name: 'jimmy',
});
// Example response
{
someKey: "I am the body example response!"
}
Mixed Example
Lets say you wanted to mock any POST request to /api/helloworld/people/:name/filter?age=28
,
with a path param of { name: 'jimmy' }
, and a json body of { occupation: 'teacher' }
module.exports = [
{
request: {
path: '/api/helloworld/people/:name/filter',
query: {
age: '28',
},
params: {
name: 'jimmy'
},
body: {
occupation: 'teacher',
},
},
response: {
data: {
someKey: "I am the mixed request example data"
}
},
},
]
// Example semi-psuedocode axios
axios.post('/api/helloworld/people/jimmy/filter?age=28', {
occupation: 'teacher',
});
// Example response
{
someKey: "I am the mixed request example data"
}
Further Reading and Examples
For more information on what the mock files look like with a mix of path params, querystrings, and request bodies, be sure to check out the example repo (Sometimes an example is worth a thousand words!)
Subset and Structural Matching with JSON Schema
genuine-mock-server
also alows for mapping mocks-to-requests using Json Schema
JSON Schema Standard
JSON Schema Node Package
This means you can match requests based on generalized subset descriptions of the incoming data, instead of EXACT matches as used by default
module.exports = [
{
request: {
path: '/api/helloworld/schema',
query: {
age: '28',
},
},
response: {
data: {
someKey: "I am the example data"
}
},
},
]
// 1. Example semi-psuedocode axios
axios.get('/api/helloworld/schema?somekey=valuehere');
// 2. Mock server compares predescribed schema, to incoming request using JSON schema
{
required: ['query'],
properties: {
query: {
properties: {
somekey: {
type: 'string',
const: 'valuehere'
}
}
}
}
}
// compared against ALL recieved request data
{
query: {
somekey: 'valuehere'
},
body: {},
params: {},
}
// 3. Example response if it matches
{
someKey: "I am the example data"
}
// 4. Otherwise, mock server keeps itterating
Gotchas and FAQs
Query String values ... they're ALWAYS strings...
When you create a mock file involving query / querystrings, be aware that values should ALWAYS be strings. This is due in part to the HTTP spec, which uses strings under-the-hood, and express itself, which parses querystring values into key value pairs (with values being strings)
Further reading on the subject and implementation from Node / Express
// inside your mock file
//GOOD!
module.exports = [
{
request: {
path: '/api/helloworld/filter',
query: {
age: '28', // Good, your mock value is a string
},
},
response: {
// ...
}
]
//BAD!
module.exports = [
{
request: {
path: '/api/helloworld/filter',
query: {
age: 28, // BAD!!! This will not match, the server will recieve 28 as a string
},
},
response: {
// ...
}
]
Exact Matches for Mocking
If you're using the default mock matching (i.e exact matching);
{
request: {
// ...
matchType: 'exact' // Genuine-mock-server assumes this matchtype by default
},
response: {
// ...
}
}
you need to be sure you're meeting all the data requirements, no more, no less.
- If you're supply a subset of the data, your mock will not be matched
- If you supply a superset of the data, your mock will not be matched
// Example of a potential problem below
const mocks = [
{
request: {
method: 'get',
path: '/api/helloworld/mismatch',
},
response: {
data: {
'key': 'If you include MORE data, I wont be matched',
}
},
},
];
// 1. Example semi-psuedocode axios
axios.get('/api/helloworld/mismatch?extra=data');
// 2. The mock server recieves a `get` request for '/api/helloworld/mismatch',
// with query data...
query: {
extra: 'data'
}
// Your mocked WILL NOT be matched, since your mock declares `query` have no data
{
request: {
method: 'get',
path: '/api/helloworld/mismatch',
// <------------ No query data provided, considered empty!
},
response: {
data: {
'key': 'If you include MORE data, I won't be matched,
}
},
},
// 3. No mock is found, as expected
If you need more nuanced generic-matching, consider using: matchType: 'schema'