Search code examples
javascriptnode.jsjestjsintegration-testingsupertest

Supertest: How to write tests for API endpoint which posts to another API endpoint?


I am using supertest to test my API endpoints. One my endpoint to which I do a post, does another post to a different endpoint and I get a 404 error even though the endpoint is implemented. This is because I am doing not a listen() in my server which is used by supertest and since supertest is not aware of this endpoint in the test, it seems logical that I get a 404. Doing a listen isn't a wise choice since I have multiple tests in multiple files and I don't want to encounter address already in use error

Once way to solve this problem was to start another server as a pretest before running the test so that the endpoint is available during the test but there should be a better approach.

This is my server

// server.js
const express = require('express')
const app = express()

// Middlewares...
// Routes...
post(/abc) // abc posts to def during the test
post(/def)

module.exports = app

This is start.js which has nothing to do with the test and just does a listen and I use this for local manual testing

// start.js
const app = require('./server.js')
app.listen(3000)

Solution

  • Here is the solution:

    server.js:

    const express = require('express');
    const request = require('request-promise');
    const app = express();
    
    app.post('/abc', async (req, res) => {
      const url = req.protocol + '://' + req.get('host');
      const rval = await request.post(`${url}/def`);
      res.send(rval);
    });
    app.post('/def', (req, res) => {
      res.send('def');
    });
    
    module.exports = app;
    

    start.js:

    const app = require('./server.js');
    const s = app.listen(3000, () => {
      console.log(`HTTP server is listening on http://localhost:${s.address().port}`);
    });
    

    server.test.js:

    const supertest = require('supertest');
    const app = require('./server');
    
    describe('server', () => {
      it('should pass', (done) => {
        supertest(app)
          .post('/abc')
          .expect(200)
          .end((err, res) => {
            if (err) throw err;
            expect(res.text).toBe('def');
            done();
          });
      });
    });
    

    Integration test result with coverage report:

     PASS  src/stackoverflow/59090082/server.test.js
      server
        ✓ should pass (54ms)
    
    -----------|----------|----------|----------|----------|-------------------|
    File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    -----------|----------|----------|----------|----------|-------------------|
    All files  |      100 |      100 |      100 |      100 |                   |
     server.js |      100 |      100 |      100 |      100 |                   |
    -----------|----------|----------|----------|----------|-------------------|
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        5.208s, estimated 13s
    

    Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59090082