satire v1.0.0-alpha.5
Satire
Intelligent mocking (of http endpoints) with a purpose.
Quick Start
- Install as a devDependency:
npm install --save-dev satire - Add an npm script:
..."scripts": { ..."mocks": "satire" } - Put
.jsonfiles in amocks/subdirectory of your project. The relative filesystem path (from themocks/subdirectory) of the.jsonfile will be the URL path the of the mock. - run
npm run mocks.
Install
Satire is intended to be installed as a development dependency.
npm install --save-dev satireInstalling Satire as a dependency allows using the javascript API or the satire CLI - if ./node_modules/.bin is in your PATH. It also ensures that your project uses the version of satire it is intended to.
However, the satire CLI can also be installed globally.
npm install -g satireFeatures
- emits events when requests are received and responses are sent
- file-based responses (particularly for GET requests)
- other HTTP methods supported through modules
- auto-reload when mock files change
- optional proxy to actual APIs
Use
Satire provides a javascript API and a command line interface. Both may be configured using command line arguments, environment variables, properties in package.json, and .${name}-rc files (using configr8). In addition, settings can be passed directly to the javascript API.
Settings
port
type: Integer
default: 0
The port you would like satire to listen on. 0 will select an open port. Positive integers will attempt to listen on the specified port. Negative integers will prevent satire from attempting to automatically start listening.
mocks
type: String, Object or Array of the same
default: ['./mocks/**/*', './test/mocks/**/*']
If a String, a glob pattern indicating where mock definitions can be found on the file system.
If an Object, it must contain a path key indicating the URI to respond to and a mock key containing a mock definition.
watch
type: Boolean|String|Object|Function
default: true
Indicates whether and how to watch the mocks found on the filesystem for changes.
If false, just load the mock definitions on startup and do not watch for changes.
If true, use the included filesystem watcher to watch for changes.
If a String, attempt to require() the module the string specifies. This module should export a Function watcher.
If an Object, attempt to require() the module specified by the module property and bind() any arguments in an Array under the args property to the Function returned by the module. The result should be a Function watcher.
If a Function, it should have the signature makeWatcher(mockGlobs). mockGlobs will be an Array. The function should return an EventEmitter which emits these events:
.on('all', eventType, filepath)- indicates that a mock definition on the filesystem has been added, updated, or removed. The mock definition will then be loaded or reloaded. If it doesn't exist, it will be removed..on('error', error)- indicates that the watcher encountered an error. The error will be re-emitted on the satire http server..on('ready')- indicates that the watcher is finished reporting (viaall) the mock definitions found on the filesystem.
Additionally, the returned EventEmitter should have a .close() method which will cause the watcher to stop watching the filesystem for changes and allow the process to potentially exit.
proxyAPIs
type: Object
default: undefined
Keys that begin with a "/" will be used to create a RegExp that searches for the key at the begining or the request URL. The first such pattern to match will be used. The value of this key should be one of:
- a
templatestring used to create the target URL for the proxy usingrequestUrl.replace(pattern, template). - a
Functionwhich take aRegExppattern - the one that defines the proxy route - and a standard Node httprequestIncomingMessage. This function should return an object containing aurland other options accepted by therequestmodule. - an
Objectwith amoduleproperty indicating a module to be required. This module should export aFunctionthat is passed the proxy object and returns a proxytemplateorFunction.
Proxy APIs have a lower priority than any mock definitions that would match the same url.
Javascript API
satire(options)
Satire exports a function to allow it to be used within your javascript program. This function will return a standard Node http server which has been set up to handle requests.
options
argv
type: Boolean
default: true
Indicates whether or not Satire should read configuration settings from process.argv
name
type: String
default: "satire"
A configuration name used to look up settings in environment variables, properties in package.json, and .${name}-rc files (using configr8).
settings
type: Object
default: undefined
A set of configuration overrides to apply. These will override any settings found through other means.
const satire = require('satire');
const server = satire({
argv: true,
name: 'my-satire-mocks',
settings: {
port: 5050,
mocks: './mocks/**/*'
// ...
}
})
.on('mock-globs', (globs) => console.log(`Mock globs: ${JSON.stringify(globs, null, 2)}`) )
.on('listening', (err) => {
const {
port
} = server.address();
console.log(`Listening on ${port}`);
})
.on('mock-start', ({ req, res }) => {
const start = process.hrtime();
console.log(`${req.method} ${req.url}`);
res.on('mock-end', ({ type, req, res }) => {
const [sec, nano] = process.hrtime(start);
const duration = `${sec}s ${nano/1000000}ms`;
if (type !== 'finish') {
console.warn(`WARN: ${req.method} ${req.url} was closed after ${duration}`);
} else {
console.log(`${req.method} ${req.url} in ${duration}`);
}
});
})
.on('error', (err) => {
console.error('\n\nERROR:', err);
process.exit(1);
});Command Line
$ satire --port 5050 --mocks "./test/mocks/**/*"Mock Definitions
Mock definitions in javascript take 1 of 4 forms:
- a
Functionthat takes an options argument withurl,location,request, andresponseproperties - a "mock descriptor" - an
Objectwith ONLYrequestandresponsekeys - anything that can
JSON.stringify() - an
Arraycontaining any of the above
Mock definitions on the filesystem may be:
- a Common JS module that exports one of the above javascript mock definitions
- a
.jsonfile which willJSON.parse()to one of the above definitions - ANY file, the binary contents of which will be loaded in to a "mock descriptor" object matching a GET request and supplying a content-type header (based on the file extention) and response body containing the data.
The standard "mock descriptor" created for arbitrary files looks like this:
request: {
method: 'GET'
},
response: {
headers: {
'content-type': mime.getType(forMockPath)
},
body: data
}- Please note that this does not attempt to limit matching a request by an
Acceptsrequest header.
Function mocks
A Function mock takes an options argument with url, location, request, and response properties.
url- theurl.parse()drequest.urllocation- the relative filesystem path where the mock definition was found. The can sometimes be useful becauseFunctionmocks can match for any path under their own.request- a standard Node httprequestIncomingMessageresponse- a standard Node httpresponse
A Function mock is the most flexible but least defined type of mock definition. At its simplest it looks something like this:
module.exports = function ({ request: req, response: res }) {
res.write('a function module');
res.end();
};If a Function mock returns an Array with the string next as it's first element Satire will continue look for mocks. Otherwise it will assume that the mock matched and handled the request.
The test/mocks/ directory contains a number of examples of this type of mock
a-fn-module/- the simple function module described abovepost-file/- a function module that saves a posted file to it's directory. This is used to test filesystem watching. It isn't at all secure; don't just copy and paste it.slow-echo/- reads a header from the request to determine a timeout after which it will respond with the request headers and body.throws/- this one just throws an error. It's useful to testing error handling.
Function mocks take precedence over any files in subdirectories under them, allowing the function mock to define how such files are accesed.
Mock Descriptors
A mock descriptor is an object with ONLY request and response properties. The request property contains a predicate against which the incoming request is matched.
The request predicate is compared against the request using these rules:
- Descend in to
Objects. All keys defined in the predicate must match. - All items in an
Arraymust match - Regular Expressions use
RegExp.test() - Functions should return
trueorfalse - All other types must be exactly equal
The request to be matched is a standard Node http request IncomingMessage. The predicate may match the url as a String or a parsed URL object.
The response object describes the response, including headers, statusCode, statusMessage, timeToRespond, and body.
An example mock descriptor looks like this:
{
request: {
method: /GET|POST/i,
headers: [
({ accept }) => /json/.test(accept),
({ authorization }) => /^Bearer\s/.test(authorization),
]
},
response: {
headers: {
"Content-Type": "application/json"
},
statusCode: 200,
body: {
imaginary: true,
value: 2
}
}
}JSON mocks
A .json file will be read and parsed from the filesystem. If it contains a "mock descriptor" it will be treated as such (see ./test/mocks/json/test-req-res.json for an example.) Otherwise, the parsed Object will be returned directly as the mock API response since it is, by definition, capable of being JSON.stringify()ed. An exmple can be seen in ./test/mocks/json/test.json.
Arrays of mocks
Sometimes you want to handle multiple types of requests at the same URI, diferentiating requests by method, headers, cookies, etc. An Array mock allows you to create a mock definition for each of these scenarios. The first mock definition in the array that matches will be used.
License
Copyright (c) 2017 Aaron Madsen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago