API testing using SuperTest

Wonder how to write tests for you API. Well you can use SuperTest which is can be used to test HTTP endpoints.

Before starting SuperTest let’s create a simple API using node + express. In express let’s use the the route given to us by them in order to learn SuperTest library. This article is not focusing on crating a rest API using express framework.

Let’s go to the /routes/users.js file and create some endpoints.

var express = require('express');
var router = express.Router();

/**
* get all users
*/
router.get('/', function (req, res, next) {
return res.json('all users sent');
});

/**
* Get a specific user
*/
router.get('/:id', function (req, res, next) {
if (req.params.id === 'U001') { // just to demo
return res.json("user U001 found");
}
return res.status(404).json('user not found');
});

/**
* Add a user
*/
router.post('/', function (req, res, next) {
let content = req.body;
if (content.id) { //just to demo
return res.status(201).json("user created");
}
return res.status(400).json('user not created');
});

module.exports = router;

I’ve crated two simple get methods to fetch user data and a post method to save a user. LOL, I was kidding there’s no actual database integration or save functionality just sending some JSON outputs based on some dummy logic 😹 and status codes to test our API. You can create a real working API. But remember to send relevant HTTP status codes so it’s easy when writing unit tests later.

Now let’s focus on writing API tests using SuperTest. First you need to install the dependency as a devDependancy.

npm install supertest --save-dev

This will include mocha as well because super test use mocha test framework. But you don’t need to install it it comes with supertest.

Then lets create a directory called test in the root level of the project and create a file called apiTest.js. This file is the file where we write the API tests.

First thing we need is the supertest module.

//apiTest.js
const request = require('supertest');

Then we need to pass the http.Server to the request() method of supertest. To do that let’s include our express app as follows.

//apiTest.js
const request = require('supertest');
const app = require('../app'); //reference to you app.js file

The let’s write the first API test to test the http://localhost:3000/users endpoint . If this endpoint works correctly it should return status code 200. That’s how the user route file is implemented . See the following code res.json() will automatically set the status code 200. So no worries.

//from users.js route file
/**
* get all users
*/
router.get('/', function (req, res, next) {
return res.json('all users sent');
});

Ok, how we write the test for this route in out apiTest.js using supertest.

//apiTest.js
const
request = require('supertest');
const app = require('../app');

//==================== user API test ====================

/**
* Testing get all user endpoint
*/
describe('GET /users', function () {
it('respond with json containing a list of all users', function (done) {
request(app)
.get('/users')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});

Here describe method is coming from mocha test framework. We simply specify the endpoint name as the first argument and then the function to write the test case. Here Mocha describe() is for grouping the test cases while it() is used to write the real test cases. We give a simple description of what we are going to test as the first argument in it() function. Then the callback function to be called.

request() needs the HTTP.server so we pass our express app reference. Then in get() we specify the route endpoint. Here we are in the app so can omit http://localhost:3000/ part. Just give the route endpoint.

in set() we set the HTTP header attributes. Then in expect() we check for the return values including Header values and body values.

In this example we check whether the returned content type is JSON. Because we send the response as JSON using res.json() method. Then expect(200) is to check the returned status code is equal to 200. Then we end the test by calling done our callback function.

Right so far so good. But how can we run this tests. Well you have to use make this change in your package.json file.

"scripts": {
"start": "node ./bin/www",
"test": "mocha 'test/apiTest.js'" //path to your test file
}

Now you can run the test cases by simply using

> npm test

After running the test file you’ll see this output.

> mocha 'test/apiTest.js'
GET /users
GET /users 200 5.289 ms - 16
✓ respond with json containing a list of all users
1 passing (38ms)

Let’s continue writing more test cases of all the endpoints in the users.js file.

const request = require('supertest');
const app = require('../app');

//==================== user API test ====================

/**
* Testing get all user endpoint
*/
describe('GET /users', function () {
it('respond with json containing a list of all users', function (done) {
request(app)
.get('/users')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});

/**
* Testing get a user endpoint by giving an existing user
*/
describe('GET /user/:id', function () {
it('respond with json containing a single user', function (done) {
request(app)
.get('/users/U001')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, done);
});
});

/**
* Testing get a user endpoint by giving a non-existing user
*/
describe('GET /user/:id', function () {
it('respond with json user not found', function (done) {
request(app)
.get('/users/idisnonexisting')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(404) //expecting HTTP status code
.expect('"user not found"') // expecting content value
.end((err) => {
if (err) return done(err);
done();
});
});
});

/**
* Testing post user endpoint
*/
describe('POST /users', function () {
let data = {
"id": "1",
"name": "dummy",
"contact": "dummy",
"address": "dummy"
}
it('respond with 201 created', function (done) {
request(app)
.post('/users')
.send(data)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(201)
.end((err) => {
if (err) return done(err);
done();
});
});
});

/**
* Testing post user endpoint
*/
describe('POST /users', function () {
let data = {
//no id
"name": "dummy",
"contact": "dummy",
"address": "dummy"
}
it('respond with 400 not created', function (done) {
request(app)
.post('/users')
.send(data)
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(400)
.expect('"user not created"')
.end((err) => {
if (err) return done(err);
done();
});
});
});

If you look closely at the code in apiTest.js file you’ll get the rhythm. I’ve only showed GET and POST methods but you can try PUT and DELETE methods with real working API.

If you run this test file using npm test command finally you’ll see an output like this.

> mocha 'test/apiTest.js'
GET /users
GET /users 200 5.610 ms - 16
✓ respond with json containing a list of all users
GET /user/:id
GET /users/U001 200 0.870 ms - 17
✓ respond with json containing a single user
GET /user/:id
GET /users/idisnonexisting 404 0.458 ms - 16
✓ respond with json user not found
POST /users
POST /users 201 10.196 ms - 14
✓ respond with 201 created
POST /users
POST /users 400 0.607 ms - 18
✓ respond with 400 not created
5 passing (62ms)

Better to refer https://github.com/visionmedia/supertest for more knowledge.

More by Chamara Madhushan Liyanage

Topics of interest

More Related Stories