- IcedFrisby API Guide
- The Basics
- Commands
- Expectations
- expectStatus(code)
- expectHeader(key, content, options)
- expectNoHeader(key)
- expectHeaderContains(key, content, options)
- expectJSON([path], json)
- expectContainsJSON([path], json)
- expectJSONTypes([path], schema)
- expectBodyContains(content)
- expectJSONLength([path], length)
- expectMaxResponseTime(ms)
- not()
- Using Paths
- useApp()
- Helpers
- Inspectors
Every frisby request begins with create(..)
and ends with toss()
or run()
.
Parameter | Description | Required |
---|---|---|
msg | A string used to name the test when it's wrapped in mocha for execution at runtime | Yes |
Used to create an instance of IcedFrisby that is used to send 1 request and receive the response.
frisby
.create('a test')
.get('http://example.com')
.expectStatus(200)
// any number of additional expect statements here
.toss()
Complete the list of commands and register the test with Mocha.
frisby
.create('a test')
.get('http://example.com')
.expectStatus(200)
// any number of additional expect statements here
.toss()
IcedFrisby hook methods like after()
and finally()
provide useful integration points for chaining tests and performing additional
assertions when the tests complete.
Run the test, returning a promise that resolves or rejects when the test
completes. This is useful for running a test within an .it()
and for
chaining workflows using async
/await
.
await frisby
.create('a test')
.get('http://example.com')
.expectStatus(200)
// any number of additional expect statements here
.run()
Parameter | Description | Required |
---|---|---|
opts | Object containing configuration options for this instance of Frisby | Yes |
Configuration object properties:
inspectOnFailure
(boolean): This is a really neat option that will help you figure out what is happening with your requests. Dumps request/response information to the logs.json
(boolean): Sets body to JSON representation of value and adds Content-type: application/json header. Additionally, parses the response body as JSON.timeout
(integer): Sets the maximum time we'll wait for a response before failing the requestretry
(integer): Number of times we'll try this request before returning a failure. If timeout is set, each retry uses the timeout.request
(object): Options for the request module. An object containing any of these: https://github.com/request/request#requestoptions-callback
Must be called before any request method (get/post/options etc.)
frisby
.create('...')
.config({ inspectOnFailure: true })
.get('...')
.expectStatus(200)
.toss()
These are the commands you'll need to get IcedFrisby talking HTTP.
An optional parameters object is accepted by each of these methods. All object parameters are optional.
{
json: 'boolean', //Whether this will be a JSON body. Overrides value set in config().
body: 'string'|'object', //The body to include in the outbound request. This overrides "data" if provided in method call.
mock: 'function', //A mock runner to use. When not provided, uses "request" (i.e. does it for real).
form: 'boolean' //Use the object in the body to create a form-encoded request.
}
Parameter | Description | Required |
---|---|---|
uri | The URI/URL being requested | Yes |
params | An optional params object, as described above | No |
Perform an HTTP GET on the specified URI.
frisby
.create('a test')
.get('http://example.com/login')
.expectStatus(200)
// any number of additional expect statements here
.toss()
Identical to get, using an HTTP HEAD request.
Identical to get, using an HTTP OPTIONS request.
Parameter | Description | Required |
---|---|---|
uri | The URI string | Yes |
data | The data to post. An object when { json: true } or { form: true } , or else a data string / buffer. |
Yes |
params | An optional params object, as described above. | No |
Perform an HTTP POST to the specified URI.
frisby
.create('a test')
.post('http://example.com/account', {
username: 'joe@example.com',
password: 'J0£_£x@mpl£',
})
.expectStatus(200)
// any number of additional expect statements here
.toss()
frisby
.create('another test')
.post(
'http://example.com/contact-us',
{
name: 'Test Person',
comment: 'What a lovely test',
},
{ form: true }
)
.expectStatus(200)
// any number of additional expect statements here
.toss()
Identical to post, using an HTTP PUT request.
Identical to post, using an HTTP PATCH request.
Identical to post, using an HTTP DELETE request.
Adds an HTTP header to your request
Parameter | Description | Required |
---|---|---|
header | String giving the name of the header. Case insensitive. | Yes |
content | String giving the value of the given header. Case sensitive. | Yes |
Note that content
is always a string, regardless of whether the data it would represent is something else (an integer or GUID for example).
This method can be repeated for the same header
value, replacing (not duplicating) the header on each successive call.
frisby
.create('a test')
.get('http://example.com')
.addHeader('Accept', 'text/html')
.expectStatus(200)
// any number of additional expect statements here
.toss()
Adds a collection of headers to your request
Parameter | Description | Required |
---|---|---|
headers | A flat object where each key becomes a header name and each corresponding value becomes that header's value in the request. As per HTTP spec, all header names are case-insensitive. | Yes |
frisby
.create('a test')
.get('http://example.com')
.addHeaders({
accept: 'text/html',
referer: 'http://www.test.net',
})
.expectStatus(200)
// any number of additional expect statements here
.toss()
Removes a given header from the outgoing request.
Parameter | Description | Required |
---|---|---|
header | String giving the name of the header to be removed | Yes |
frisby
.create('Request with stripped headers')
.get('http://example.com')
.removeHeader('Content-Type')
.toss()
In this example, the Content-Type is always set by IcedFrisby, but the test developer doesn't want to send it so they can validate whether their API is resilient to such things.
Sets the basic auth header for the request.
Parameter | Description | Required |
---|---|---|
username | String | Yes |
password | String | Yes |
isDigest | Boolean. Defaults to false . When given and true , will configure IcedFrisby to send the request initially without the auth header, then repeat the request with the auth header when challenged with an HTTP/401 that has a WWW-Authenticate header |
No |
frisby
.create('Get secret things')
.get('http://example.com/secret/things')
.auth('bob', 'letmein')
.expectStatus(200)
.toss()
As an alternative, you can use http://username:password@example.com
.
IcedFrisby provides a lot of helper functions to help you check the most common aspects of REST API testing.
Use the expect functions after create() and before toss().
frisby
.create('a test')
.get('http://example.com')
.expectStatus(200)
// any number of additional expect statements here
.toss()
Tests the HTTP response Status code.
Parameter | Description | Required |
---|---|---|
code | Integer representing the HTTP status code expected to be on the response. | Yes |
frisby
.create('Ensure we are dealing with a teapot')
.get('http://httpbin.org/status/418')
.expectStatus(418)
.toss()
Note: IcedFrisby represents all network timeouts as a response of HTTP/599.
Tests that a single HTTP response header has the exact content or matches a regex. Key comparisons are case-insensitive. Content comparisons are case-insensitive for strings, case-sensitive for regular expressions.
Parameter | Description | Required |
---|---|---|
key | String. Name of the header. | Yes |
content | String or RegExp to be matched. | Yes |
options | Object. When containing allowMultipleHeaders: true , will check each parameter with this name and succeed if 1 or more matches. By default, multiple headers containing this name will raise an error. |
No |
String example:
frisby
.create('Ensure response has a proper JSON Content-Type header')
.get('http://httpbin.org/get')
.expectHeader('Content-Type', 'application/json')
.toss()
frisby
.create(
'Ensure response has JSON somewhere in the Content-Type header via regex'
)
.get('http://httpbin.org/get')
.expectHeader('Content-Type', /.*json.*/)
.toss()
frisby
.create('Ensure response returns one cookie called "auth"')
.post('http://example.com/login', { username: admin, password: example })
.expectHeader('Set-Cookie', /^auth=/, { allowMultipleHeaders: true })
.toss()
For backwards compatibility, expectHeaderToMatch(key, pattern)
is an alias for this function (but does not accept the options parameter).
Tests that a specific HTTP header was not received in the response
Parameter | Description | Required |
---|---|---|
key | String. Name of the header. | Yes |
frisby
.create('Ensure response has no Set-Cookie header')
.get('http://httpbin.org/get')
.expectNoHeader('Set-Cookie')
.toss()
Tests that a single HTTP response header contains the specified content. Both key and content comparisons are case-insensitive.
Parameter | Description | Required |
---|---|---|
key | String. Name of the header. | Yes |
content | String to be matched. | Yes |
options | Object. When containing allowMultipleHeaders: true , will check each parameter with this name and succeed if 1 or more matches. By default, multiple headers containing this name will raise an error. |
No |
frisby
.create('Ensure response has JSON somewhere in the Content-Type header')
.get('http://httpbin.org/get')
.expectHeaderContains('Content-Type', 'json')
.toss()
frisby
.create('Ensure response returns one cookie called "auth"')
.post('http://example.com/login', { username: admin, password: example })
.expectHeader('Set-Cookie', 'auth=', { allowMultipleHeaders: true })
.toss()
Tests that response body is JSON and deeply equals the provided JSON.
Parameter | Description | Required |
---|---|---|
path | String. Path to the the subset of the response JSON to be tested. | No |
json | Object. The JSON to test against. | Yes |
For info on the path parameter, see Using Paths.
frisby
.create('Ensure test has foo and bar')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.expectJSON('args', {
args: {
foo: 'bar',
bar: 'baz',
},
})
.toss()
Tests that response body is JSON and contains a subset of the provided JSON.
Parameter | Description | Required |
---|---|---|
path | String. Path to the the subset of the response JSON to be tested. | No |
json | Object. The JSON to test against. | Yes |
For info on the path parameter, see Using Paths.
frisby
.create('Ensure test has foo and bar')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.expectContainsJSON('args', {
foo: 'bar',
})
.toss()
Validates the response body against the provided Joi schema.
Parameter | Description | Required |
---|---|---|
path | String. Path to the the subset of the response JSON to be tested. | No |
schema | Joi schema that the response JSON should conform to. |
Yes |
For info on the path parameter, see Using Paths.
frisby
.create('Ensure response has proper JSON types in specified keys')
.post('http://httpbin.org/post', {
arr: [1, 2, 3, 4],
foo: 'bar',
bar: 'baz',
answer: 42,
})
.expectJSONTypes(
'args.json',
Joi.object().keys({
arr: Joi.array().items(Joi.number()).required(),
foo: Joi.string().required(),
bar: Joi.string().required(),
answer: Joi.number().integer().required(),
})
)
.toss()
Tests that the HTTP response body contains the provided content string. Used for testing HTML, text, or other content types.
Parameter | Description | Required |
---|---|---|
content | String or RegExp that the body will be tested against | Yes |
frisby
.create(
'Ensure this is *actually* a real teapot, not some imposter coffee pot'
)
.get('http://httpbin.org/status/418')
.expectStatus(418)
.expectBodyContains('teapot')
.toss()
Tests given path or full JSON response for specified length. When used on objects, the number of keys are counted. When used on other JavaScript types such as Arrays or Strings, the native length property is used for comparison.
Parameter | Description | Required |
---|---|---|
length | Integer representing expected length, e.g. 3 OR String reprenting a condition, e.g. >10 . Can be any of < , <= , > , >= |
Yes |
frisby
.create(
'Ensure "bar" really is only 3 characters... because you never know...'
)
.get('http://httpbin.org/get?foo=bar&bar=baz')
.expectJSONLength('args.foo', 3)
.toss()
Tests that the HTTP response arrives within a given number of milliseconds
Parameter | Description | Required |
---|---|---|
ms | Integer representing number of milliseconds as the max threshold for the response | Yes |
frisby
.create('Ensure response arrives within two seconds')
.get('http://httpbin.org/get')
.expectMaxResponseTime(2000)
.toss()
Negates all expectJSON
, expectJSONTypes
, expectContainsJSON
and expectJSONLength
expects in this test, inverting the logic to expect the opposite, e.g. JSON doesn't contain this.
frisby
.create('Check deleted item no longer exists')
.get('http://example.com/things/list')
.not()
.expectContainsJSON('*', { name: 'Jane Doe' })
.toss()
Paths are used in the following IcedFrisby functions:
expectJSON
expectContainsJSON
expectJSONTypes
expectJSONLength
The path parameter can be used to test a nested JSON object.
frisby
.create('Ensure response has proper JSON types in specified keys')
.post('http://httpbin.org/post', {
answer: 42,
})
.expectJSONTypes(
'args.json',
Joi.object().keys({
answer: Joi.number().integer().required(),
})
)
.toss()
This example returns a REST response with { args: { json: { answer: 42 } } }
. Using a path of args.json
allows testing of a nested JSON object, { answer: 42 }
. This is useful when you don't care about other parts of the response.
To test all objects in an array, use an asterisk character, so the path looks like 'args.path.myarray.*'
. If the array is at the root level, use '*'
as the path.
This path mode is often combined with expectJSONTypes to ensure each item in an array contains all required keys and types.
// some request that returns:
// [
// {
// number: 5,
// string: 'a string'
// },
// {
// number: 6,
// string: 'another string'
// }
// ]
.expectJSONTypes('*', Joi.object().keys({
number: Joi.number().required(),
string: Joi.string().required(),
boolean: Joi.boolean().forbidden()
}))
.toss();
To test a single object in an array, use a question mark character, so the path looks like 'args.path.myarray.?'
. If the array is at the root level, use '?'
as the path.
// some request that returns:
// [
// {
// number: 5
// },
// {
// string: 'a string'
// }
// ]
.expectJSONTypes('?', Joi.object().keys({
string: Joi.string().required()
})
.toss();
IcedFrisby provides the useApp(app, basePath)
function to bootstrap a Node.js http.Server-based application. Provide your app
object and IcedFrisby will start the Express/Koa/etc application and proceed to test against the application.
This is similar to supertest's request function:
You may pass an http.Server, or a Function to request() - if the server is not already listening for connections then it is bound to an ephemeral port for you so there is no need to keep track of ports.
- Types:
app
:http.Server
,basePath
:string
- Defaults:
app
:none
,basePath
:''
var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
// prevent the app from starting if it is required as a module (it is in this example!)
if (!module.parent) {
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
})
}
module.exports = app // export the application
var app = require('./app')
describe('Express app integration', function () {
frisby
.create('should start the app and request')
.useApp(app)
.get('/')
.expectStatus(200)
.expectBodyContains('Hello World!')
.toss()
})
Callback function to run before the tested request is executed. Can be used to set up a test environment or even to launch a server. If an argument is provided, it is assumed to be a callback function, similar to Mocha's before(). Useful for writing plugins. Multiple registered functions are run in order of registration.
frisby
.create('Upcheck test')
.before(function () {
this._pluginContext = 123
})
.before(function (done) {
http.createServer().listen(80, done)
})
.get('http://localhost/upCheck')
.expectStatus(200)
.toss()
Callback function to run after test is completed successfully. Can be used to run tests sequentially. If an extra argument is provided, it is assumed to be a callback function, similar to Mocha's after()
. Multiple registered functions are run in order of registration.
frisby
.create('First test')
.get('http://httpbin.org/get?foo=bar')
.after(function (err, res, body, headers, done) {
// async, don't forget to invoke done()
setImmediate(done)
})
.after(function (err, res, body, headers) {
frisby
.create('Second test, run after first is completed')
.get('http://httpbin.org/get?bar=baz')
.toss()
})
.toss()
Callback function to run after test is done, either successfully or not. Can be used to tear down a test context established with before()
. If an extra argument is provided, it is assumed to be a callback function, similar to Mocha's after()
. Useful for writing plugins. Multiple registered functions are run in order of registration.
frisby
.create('First test')
.get('http://httpbin.org/get?foo=bar')
.finally(function () {
// sync
})
.finally(function (done) {
// async, don't forget to invoke done()
setImmediate(done)
})
.toss()
Callback function to run after test is completed. This helper function automatically converts the response body to JSON.
frisby.create('First test')
.get('http://httpbin.org/get?foo=bar')
.afterJSON(function(json) {
// Now you can use 'json' in additional requests
frisby.create('Second test, run after first is completed')
.get('http://httpbin.org/get?bar=' + json.args.foo)
.toss()
});
.toss()
When running in Mocha, run this test exclusively. When toss()
is invoked, the
test is wrapped in a Mocha describe.only
block instead of a describe
block.
Has no effect on run()
.
Skip this test. When run()
is invoked, do nothing. When toss()
is invoked,
the test is wrapped in a Mocha describe.skip
block instead of a describe
block. skip()
takes precedence over only()
.
When condition is true, skip this test. run()
will do nothing and toss()
will wrap the test in describe.skip
.
When predicate evaluates to true, skip this test. This allows conditional skipping based on further manipulation of the Frisby object later in the call chain (e.g. for conditionally skipping tests with intercepts).
Sets the timeout for this request.
Parameter | Description | Required |
---|---|---|
ms | Integer. Sets the timeout for this individual request. | Yes |
frisby
.create('Long-running request')
.get('http://example.com/slowthing')
.timeout(5000)
.expectStatus(200)
.toss()
When a timeout occurs, the test will be aborted. The expectations and inspections may or may not run and will not be printed. If you want to run all the expectations and inspections even when the response is slow, use expectMaxResponseTime instead.
If this were used in conjunction with retries, each retry would have this configured timeout.
This function can also be called with no parameter to return the current configured timeout (either by default, by global setup or having used this function with a parameter previously).
Set the number of (and additional backoff between) retries for this test. Each retry will be the configured timeout plus (retry number x backoff) apart.
Parameter | Description | Required |
---|---|---|
count | Integer. Number of times to retry (meaning that 0 is still a single attempt) | Yes |
backoff | Integer. Number of milliseconds to add to the wait between each successive retry. Defaults to 1000ms. | No |
frisby
.create('Get a flaky thing')
.get('http://example.com/thing-that-sometimes-responds')
.expectStatus(200)
.retry(2, 250)
.toss()
In this above example, this makes two retries, for a total of three attempts, waiting 250 ms between them. For each attempt, the timeout resets to 5000 ms (the default).
Set the root URI/URL that will be prepended to every request, replacing anything set by request.baseUri
in global setup.
frisby
.create('Simple Get')
.baseUri('http://httpbin.org')
.get('/get?foo=bar')
.expectStatus(200)
.toss()
Sets a period of time in milliseconds to wait after the test starts (and any before() hook processing) until the request is sent. This can allow time for server-side processing between chained requests.
const myUser = { name: 'Jane Doe' }
frisby
.create('Create Item')
.post('http://example.com/users/create', myUser)
.after(function (err, res, body, headers) {
frisby
.create('Check Item Exists')
.get('http://example.com/users/list')
.waits(500)
.expectJSON('?', myUser)
.toss()
})
.toss()
Sets a function to run if an error is raised. Can be used to output additional debug info not covered by the inspectors, or perhaps to add validation to a non-deterministic result.
frisby
.create('Expecting something from nothing')
.get('http://example.com/empty')
.expectBodyContains('foo')
.exceptionHandler(err => {
expect(err).to.be.an.instanceof(AssertionError) //Asserts that this came from a failing "expect" function
expect(err.message).to.equal("expected '' to include 'foo'")
})
.toss()
Inspectors are useful for viewing details about HTTP requests and responses in the console.
Note that .config({ inspectOnFailure: true })
is a really neat option that will help you figure out what is happening with your requests. Dumps request/response information to the logs.
Provides access to request and response data before expectations are executed. This should not be used for assertions. Use after() for more assertions.
Parameter | Description | Required |
---|---|---|
cb | Callback function to be called before any expects are run. Signature: function(err, req, res, body, headers){..} |
Yes |
Callback Parameters:
Parameter | Description |
---|---|
err | Error object if there was an error making the request. Will be null if no error is present. |
req | request object IcedFrisby made to the endpoint |
res | response object received from the endpoint |
body | body object (a part of the response) |
headers | headers object (a part of the response) |
frisby
.create('Inspecting some data')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.inspect(function (err, req, res, body, headers) {
console.log('Got args:' + body.args)
})
.toss()
Inspects the entire request object sent from IcedFrisby.
Parameter | Description | Required |
---|---|---|
message | An optional to print before the inspection | No |
frisby
.create('Just a quick inspection of the JSON HTTP response')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.inspectRequest()
.toss()
Inspects the entire response.
Parameter | Description | Required |
---|---|---|
message | An optional to print before the inspection | No |
frisby
.create('Just a quick inspection of the JSON HTTP response')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.inspectResponse()
.toss()
Inspects the response headers.
Parameter | Description | Required |
---|---|---|
message | An optional to print before the inspection | No |
frisby
.create('Just a quick inspection of the JSON HTTP response')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.inspectHeaders()
.toss()
Console output:
{ server: 'nginx',
date: 'Sun, 17 May 2015 02:38:21 GMT',
'content-type': 'application/json',
'content-length': '188',
connection: 'close',
'access-control-allow-origin': '*',
'access-control-allow-credentials': 'true' }
Dumps parsed JSON body to the console.
Parameter | Description | Required |
---|---|---|
message | An optional to print before the inspection | No |
frisby
.create('Just a quick inspection of the JSON HTTP response')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.inspectJSON()
.toss()
Console output:
{ url: 'http://httpbin.org/get?foo=bar&bar=baz',
headers:
{ 'Content-Length': '',
'X-Forwarded-Port': '80',
Connection: 'keep-alive',
Host: 'httpbin.org',
Cookie: '',
'Content-Type': 'application/json' },
args: { foo: 'bar', bar: 'baz' },
origin: '127.0.0.1' }
Dumps the raw response body to the console without any parsing.
Parameter | Description | Required |
---|---|---|
message | An optional string to print before the inspection | No |
// Test
frisby
.create('Very useful for HTML, text, or raw output')
.get('http://asciime.heroku.com/generate_ascii?s=Frisby.js')
.inspectBody()
.toss()
Console output:
______ _ _ _
| ____| (_) | | (_)
| |__ _ __ _ ___| |__ _ _ _ ___
| __| '__| / __| '_ \| | | | | / __|
| | | | | \__ \ |_) | |_| |_| \__ \
|_| |_| |_|___/_.__/ \__, (_) |___/
__/ |_/ |
|___/|__/
Inspects the response status.
Parameter | Description | Required |
---|---|---|
message | An optional string to print before the inspection | No |
frisby
.create('Just a quick inspection of the JSON HTTP response')
.get('http://httpbin.org/get?foo=bar&bar=baz')
.inspectStatus()
.toss()
By default, IcedFrisby sends POST and PUT requests as application/x-www-form-urlencoded
parameters. If you want to send a raw request body or actual JSON, use { json: true }
as the third argument (object literal of options).
frisby
.create('Post JSON string as body')
.post(
'http://httpbin.org/post',
{
arr: [1, 2, 3, 4],
foo: 'bar',
bar: 'baz',
answer: 42,
},
{ json: true }
)
.expectHeaderContains('Content-Type', 'json')
.toss()