Skip to content

Instantly share code, notes, and snippets.

@paitonic
Last active April 19, 2021 14:16
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save paitonic/960d8c721455333a154fa1f73b412d51 to your computer and use it in GitHub Desktop.
Save paitonic/960d8c721455333a154fa1f73b412d51 to your computer and use it in GitHub Desktop.
ExpressJS - API Tests with Jasmine and request

ExpressJS - API Tests with Jasmine and request

What am I trying to solve

I was trying to solve an issue with starting the ExpressJS server before each single test and closing it after each test completes, just to make sure each test is running under the same conditions.

Here is the error I ran into while trying to run the tests

$ ./node_modules/jasmine/bin/jasmine.js
Started
started
events.js:160
      throw er; // Unhandled 'error' event
      ^

Error: listen EADDRINUSE :::3000

Jasmine fails on second test because my code didn't handle start/close properly on each test.

Dependencies:

$ npm install jasmine request --save-dev

Dummy project structure

.
├── routes
│   ├── city.js
│   └── people.js
├── server.js
└── spec
    ├── citySpec.js
    ├── globalSpec.js
    ├── peopleSpec.js

Code

server.js

function run(callback) {
    const express = require('express');
    const bodyParser = require('body-parser');


    const city = require('./routes/city');
    const people = require('./routes/people');
    const app = express();

    app.use(bodyParser.json());
    app.use(city);
    app.use(people);

    var server = app.listen(3000, function () {
        console.log('started');

        if (callback) {
            callback();
        }
    });

    server.on('close', function () {
        console.log('closed');
    });

    return server;
}

if (require.main === module) {
    run();
}

exports.run = run;

server.js exposes run function which is responsible for running the server and returning the node's http.Server object (also, see app.listen).

run function accepts an optional callback function, it will be called after the server started running, later, we will see how this helps us.

Wrapping all code inside function allows us to create server on demand but it breaks our ability to run it directly via command line because run is not called inside server.js, therefore, that's why there is an if statement at the end that checks if run should be called whenserver.js runs directly, and not to be called when required as module, see require.main.

routes/city.js

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


router.get('/city', function(request, response) {
  response.status(200).json({'city': 'Tel-Aviv'});
});

module.exports = router;

routes/people.js

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


router.get('/people', function(request, response) {
  response.status(200).json([{'name': 'Cate Blanchett'}]);
});

module.exports = router;

Tests

spec/citySpec.js

const request = require('request');
const server = require('../server');

const endpoint = 'http://localhost:3000/city';

describe('city', function () {
    it('should return 200 response code', function (done) {
        request.get(endpoint, function (error, response) {
            expect(response.statusCode).toEqual(200);
            done();
        });
    });

    it('should fail on POST', function (done) {
        request.post(endpoint, {json: true, body: {}}, function (error, response) {
            expect(response.statusCode).toEqual(404);
            done();
        });
    });
});

spec/peopleSpec.js

const request = require('request');
const server = require('../server');

const endpoint = 'http://localhost:3000/people';

describe('people', function () {
    it('should return 200 response code', function (done) {
        request.get(endpoint, function (error, response) {
            expect(response.statusCode).toEqual(200);
            done();
        });
    });

    it('should fail on POST', function (done) {
        request.post(endpoint, {json: true, body: {}}, function (error, response) {
            expect(response.statusCode).toEqual(404);
            done();
        });
    });
});

spec/globalSpec.js

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


var serverInstance;

beforeEach(function (done) {
    serverInstance = server.run(done);
});

afterEach(function (done) {
    serverInstance.close(done);
});

Before each test we will create a new server instance and pass jasmine's done function to run, so when server starts running and ready to accept requests it will call done, lastly, when that happens, only then beforeEach() will complete running and next test will start.

After each test we will close the server by calling close method and passing done as callback.

Result

$ ./node_modules/jasmine/bin/jasmine.js
Started
started
closed
.started
closed
.started
closed
.started
closed
.


4 specs, 0 failures
Finished in 0.138 seconds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment