DEV Community

Cover image for How to use the Open API and Swagger Validator plugin for Chai and Jest to keep your API documentation up to date
James Wallis
James Wallis

Posted on

How to use the Open API and Swagger Validator plugin for Chai and Jest to keep your API documentation up to date

In a previous post (the first in this series), I introduced the open-source OpenAPIValidators project that contains plugins to the Jest and Chai testing frameworks to ensure that an OpenAPI Specification matches the corresponding API implementation.

In this post, I want to demonstrate the OpenAPI Response Validator plugin by creating a small sample application. Hopefully, by the end, I'll have convinced you to add the Chai or Jest plugin to your toolkit so that your documentation always matches exactly how your API operates.

 Demonstration

All the code for this tutorial is available on GitHub

 Setup

  1. If you're starting from scratch run npm init, otherwise skip this step.
  2. Install the required dependencies for your application. For the demo application I need Express - npm install -s express.
  3. Next, install supertest, to make API calls in your tests, and either chai and chai-openapi-response-validator or jest and jest-openapi depending on whether your preference (I'll be using jest).

In short copy one of these into your terminal:

# FOR CHAI
npm i -s supertest chai chai-openapi-response-validator

# OR

# FOR JEST
npm i -s supertest jest jest-openapi
Enter fullscreen mode Exit fullscreen mode

Creating the application

You want to create two files:

  1. server.js - Where the API functions will live. The demo code contains a few different APIs to exhibit different OpenAPI definitions. Exporting the Express app enables it to be used alongside supertest in the tests.
  2. app.js - Requires server.js and starts the server on a port.

server.js should contain the following:

const express = require('express')
const app = express()

app.get('/hello', (req, res) => {
  res.status(200).send('Hello World!')
});

app.post('/hello', (req, res) => {
  res.sendStatus(202);
});

app.get('/customcode', (req, res) => {
  res.status(418).send(`I'm a teapot`);
});

app.get('/object',(req, res) => {
  res.status(200).send({
    propertyShouldExist: true,
  });
});

module.exports = app;
Enter fullscreen mode Exit fullscreen mode

app.js should contain the following:

const server = require('./server');
const port = 3000;

server.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})
Enter fullscreen mode Exit fullscreen mode

You can run this by node app.js and open it in a browser on http://localhost:3000.

 Creating the OpenAPI Specification

Now that the API endpoints are created, we can create an OpenAPI Specification file (openapi.yml) which describes how they should operate.

Create an openapi.yml file with the following contents:

openapi: 3.0.3
info:
  title: Open Api Validator Example
  version: 0.0.1
paths:
  /hello:
    get:
      responses:
        200:
          description: Response body should contain a string
          content:
            text/plain:
              schema:
                type: string
                example: Hello World
    post:
      responses:
        202:
          description: Accepted
          content:
            text/plain:
              schema:
                type: string
                example: Accepted
  /customcode:
    get:
      responses:
        418:
          description: Response code I'm a teapot
          content:
            text/plain:
              schema:
                type: string
                example: I'm a teapot
  /object:
    get:
      responses:
        200:
          description: Response code I'm a teapot
          content:
            application/json:
              schema:
                type: object
                required:
                  - propertyShouldExist
                properties:
                  propertyShouldExist:
                    type: boolean

Enter fullscreen mode Exit fullscreen mode

To understand the yaml above, take the first path/response definition:

paths:
  /hello:                // The API path
    get:                 // The verb (get, post, put, delete etc)
      responses:         // An array of responses using the HTTP status code as a key
        200:             // A HTTP status code
          description: Response body should contain a string
          content:       // What will be returned by the API
            text/plain:
              schema:
                type: string
                example: Hello World
Enter fullscreen mode Exit fullscreen mode

By this point, you should have an Express server setup with a simple API and an openapi.yml which can be used to determine how the API operates - what paths it contains and what it should return.

 Writing the tests

Finally, let's write a test to assert that the Express API matches the OpenAPI Specification. This test will use the OpenAPI Specification explained above (GET request for /hello).

Add the following to server.test.js:

const jestOpenAPI = require('jest-openapi');
const request = require('supertest');
const path = require('path');
const server = require('./server');

// Sets the location of your OpenAPI Specification file
jestOpenAPI(path.join(__dirname, './openapi.yml'));

describe('server.js', () => {
  it('should make a GET request and satisfy OpenAPI spec', async () => {
    // Make request (supertest used here)
    const res = await request(server).get('/hello');
    // Make any assertions as normal
    expect(res.status).toEqual(200);
    // Assert that the HTTP response satisfies the OpenAPI spec
    expect(res).toSatisfyApiSpec();
  });
});
Enter fullscreen mode Exit fullscreen mode

You'll also want to add:

"scripts": {
    "test": "jest"
  },
Enter fullscreen mode Exit fullscreen mode

to your package.json so that npm run test will run your tests using Jest.

Running the tests

Run it with npm run test.

The tests should pass the first time:

Passing demo test

Now let's edit the Express app in server.js to return a response that isn't documented in the OpenAPI Specification. We can do this by changing the /hello GET request to return an object rather than a string.

app.get('/hello', (req, res) => {
  res.status(200).send({ key: 'property' })
});
Enter fullscreen mode Exit fullscreen mode

When you run the tests, they should fail as the actual response does not match the specification:

Failing demo test

If you revert that change, you will see that the tests pass.

Finally, we can add more tests to the server.test.js file to ensure we test each endpoint against the OpenAPI Specification. Change add the following into your existing describe block.

  it('should make a POST request and satisfy OpenAPI spec', async () => {
    const res = await request(server).post('/hello');

    expect(res.status).toEqual(202);

    // Assert that the HTTP response satisfies the OpenAPI spec
    expect(res).toSatisfyApiSpec();
  });

  it('should make a GET request, receive a 418 code and satisfy OpenAPI spec', async () => {
    const res = await request(server).get('/customcode');
    expect(res.status).toEqual(418);
    // Assert that the HTTP response satisfies the OpenAPI spec
    expect(res).toSatisfyApiSpec();
  });

  it('should make a GET request, receive an object and satisfy OpenAPI spec', async () => {
    const res = await request(server).get('/object');

    expect(res.status).toEqual(200);

    // Assert that the HTTP response satisfies the OpenAPI spec
    expect(res).toSatisfyApiSpec();
  });
Enter fullscreen mode Exit fullscreen mode

and your tests should output:

All tests passing

Congrats, having set up the above, if your OpenAPI specification becomes outdated your tests will fail and you'll know to update the documentation. In turn, you'll save a future developer time trying to use an API without matching documentation.

 Summary

In this post, I've demonstrated the chai-openapi-response-validator and jest-openapi test packages which will improve your tests by checking your API responses against an OpenAPI Specification.

By using those packages you can ensure that your API always has an up-to-date OpenAPI Specification that can be consumed by Swagger.io and used by consumers of your API to ensure that they are making the correct API calls with the correct parameters - helping to remove frustration in the long run when documentation becomes outdated.

Any comments or questions let me know below. If you liked this article react or follow me!

Thanks for reading!

Top comments (4)

Collapse
 
hggonzalezdev profile image
Gabriel Gonzalez

In my case I'm using OpenAPI 2 Spec but no matter how I try to make the GET or POST request I always get a 404 error instead of 200 or 201, I use swagger online editor and postman and the problem doesn't seen to be the services or endpoints. Not sure if it has to do with the fact that I'm using OpenAPI 2 instead of 3

Collapse
 
jameswallis profile image
James Wallis

Hi, the plugin should support OpenAPI 2 Spec. Here is a valid v2 spec that they use to test. It's possible that you've found a bug so I'd recommend raising it on GitHub.

Collapse
 
hggonzalezdev profile image
Gabriel Gonzalez

In that example they specify the path in the json file, but in my case I'm doing it using express-openapi and I define the paths on each controller I create, could that be an issue at the moment of testing?

Collapse
 
rwalle61 profile image
Richard Waller

Thanks for the write up! Clear and easy to follow