Simple Chai support for asserting that HTTP responses satisfy an OpenAPI spec.
If your server's behaviour doesn't match your API documentation, then you need to correct your server, your documentation, or both. The sooner you know the better.
This plugin lets you automatically test whether your server's behaviour and documentation match. It extends the Chai Assertion Library to support the OpenAPI standard for documenting REST APIs.
- Validates the status and body of HTTP responses against your OpenAPI spec (see example)
- Validates objects against schemas defined in your OpenAPI spec (see example)
- Load your OpenAPI spec just once in your tests (load from a filepath or object)
- Supports OpenAPI 2 and 3
- Supports OpenAPI specs in YAML and JSON formats
- Supports
$ref
in response definitions (i.e.$ref: '#/definitions/ComponentType/ComponentName'
) - Informs you if your OpenAPI spec is invalid
- Supports responses from
axios
,request-promise
,supertest
,superagent
, andchai-http
This is an addon plugin for the Chai Assertion Library. Install via npm.
$ npm install --save-dev chai-openapi-response-validator
// Set up Chai
const chai = require('chai');
const expect = chai.expect;
// Import this plugin
const chaiResponseValidator = require('chai-openapi-response-validator');
// Load an OpenAPI file (YAML or JSON) into this plugin
chai.use(chaiResponseValidator('path/to/openapi.yml'));
// Write your test (e.g. using Mocha)
describe('GET /example/endpoint', function() {
it('should satisfy OpenAPI spec', async function() {
// Get an HTTP response from your server (e.g. using axios)
const res = await axios.get('http://localhost:3000/example/endpoint');
expect(res.status).to.equal(200);
// Assert that the HTTP response satisfies the OpenAPI spec
expect(res).to.satisfyApiSpec;
});
});
openapi: 3.0.0
info:
title: Example API
version: 1.0.0
paths:
/example:
get:
responses:
200:
description: Response body should be an object with fields 'stringProperty' and 'integerProperty'
content:
application/json:
schema:
type: object
required:
- stringProperty
- integerProperty
properties:
stringProperty:
type: string
integerProperty:
type: integer
// Response includes:
{
status: 200,
body: {
stringProperty: 'string',
integerProperty: 123,
},
};
// Response includes:
{
status: 200,
body: {
stringProperty: 'string',
integerProperty: 'invalid (should be an integer)',
},
};
AssertionError: expected res to satisfy API spec:
{
message: 'The response was not valid.',
errors: [
{
path: 'integerProperty',
errorCode: 'type.openapi.responseValidation',
message: 'integerProperty should be integer'
}
],
actualResponse: {
status: 200,
body: {
stringProperty: 'string',
integerProperty: 'invalid (should be an integer)'
}
}
}
// Set up Chai
const chai = require('chai');
const expect = chai.expect;
// Import this plugin
const chaiResponseValidator = require('chai-openapi-response-validator');
// Load an OpenAPI file (YAML or JSON) into this plugin
chai.use(chaiResponseValidator('path/to/openapi.yml'));
// Write your test (e.g. using Mocha)
describe('myModule.getObject()', function() {
it('should satisfy OpenAPI spec', async function() {
// Run the function you want to test
const myModule = require('path/to/your/module.js');
const output = myModule.getObject();
// Assert that the output satisfies a schema defined in your OpenAPI spec
expect(output).to.satisfySchemaInApiSpec('ExampleSchemaObject');
});
});
openapi: 3.0.0
info:
title: Example API
version: 1.0.0
paths:
/example:
get:
responses:
200:
description: Response body should be an ExampleSchemaObject
content:
application/json:
schema: '#/components/schemas/ExampleSchemaObject'
components:
schemas:
ExampleSchemaObject:
type: object
required:
- stringProperty
- integerProperty
properties:
stringProperty:
type: string
integerProperty:
type: integer
// object includes:
{
stringProperty: 'string',
integerProperty: 123,
};
// object includes:
{
stringProperty: 123,
integerProperty: 123,
};
AssertionError: expected object to satisfy schema 'ExampleSchemaObject' defined in API spec:
{
message: 'The object was not valid.',
errors: [
{
errorCode: 'type.openapi.objectValidation',
message: 'stringProperty should be string'
}
],
actualObject: {
{
stringProperty: 123,
integerProperty: 123
}
}
}
1. From an absolute filepath (see above)
// Set up Chai
const chai = require('chai');
const expect = chai.expect;
// Import this plugin
const chaiResponseValidator = require('chai-openapi-response-validator');
// Get an object representing your OpenAPI spec
const openApiSpec = {
openapi: '3.0.0',
info: {
title: 'Example API',
version: '0.1.0',
},
paths: {
'/example/endpoint': {
get: {
responses: {
'200': {
description: 'Response body should be a string',
content: {
'application/json': {
schema: {
type: 'string',
},
},
},
},
},
},
},
},
};
// Load that OpenAPI object into this plugin
chai.use(chaiResponseValidator(openApiSpec));
// Write your test (e.g. using Mocha)
describe('GET /example/endpoint', function() {
it('should satisfy OpenAPI spec', async function() {
// Get an HTTP response from your server (e.g. using axios)
const res = await axios.get('http://localhost:3000/example/endpoint');
expect(res.status).to.equal(200);
// Assert that the HTTP response satisfies the OpenAPI spec
expect(res).to.satisfyApiSpec;
});
});
// Set up Chai
const chai = require('chai');
const expect = chai.expect;
// Import this plugin
const chaiResponseValidator = require('chai-openapi-response-validator');
// Write your test (e.g. using Mocha)
describe('GET /example/endpoint', function() {
// Load your OpenAPI spec from a web endpoint
before(async function() {
const axios = require('axios');
const response = await axios.get('url/to/openapi/spec');
const openApiSpec = response.data; // e.g. { openapi: '3.0.0', <etc> };
chai.use(chaiResponseValidator(openApiSpec));
});
it('should satisfy OpenAPI spec', async function() {
// Get an HTTP response from your server (e.g. using axios)
const res = await axios.get('http://localhost:3000/example/endpoint');
expect(res.status).to.equal(200);
// Assert that the HTTP response satisfies the OpenAPI spec
expect(res).to.satisfyApiSpec;
});
});
Thank you very much for considering to contribute!
Please make sure you follow our Code Of Conduct and we also strongly recommend reading our Contributing Guide.