Search code examples
node.jsexpressjestjsintegration-testing

Why Jest doesn't recognise Server instance with Express.js?


First, I'm a bit of a beginner. I created an Express project using express-generator which contains a file: bin/www. www is actually a javascript file containing the server creation like below code snippet:

#!/usr/bin/env node

// This file is generated by express generator

var app = require('../app');
var debug = require('debug')('happy-meal:server');
var http = require('http');


var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

var server = http.createServer(app);

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

function normalizePort(val) {}
function onError(error) {}
function onListening() {}

// Added by myself
exports.module = server;

The last line I added myself to get access to Server instance in jest test.

So, I want to run a simple integration test with the following code:

// integration test: users.test.js
const request = require("supertest");

describe("/api/v1/users", () => {

    let server;

    beforeEach(() => {
        server = require("../../bin/www");
    });
    afterEach(() => {
        server.close();
    });

    describe("GET /", () => {
        it("should return all users", async (done) => {
            const res = await request(server).get("/api/v1/users");

            expect(res.status).toBe(200);
            done();
        });
    });
});

This resulted in the following error output of jest:

TypeError: app.address is not a function

      14 |     describe("GET /", () => {
      15 |         it("should return all users", async (done) => {
    > 16 |             const res = await request(server).get("/api/v1/users");
         |                                               ^
      17 | 
      18 |             expect(res.status).toBe(200);
      19 |             done();

      at Test.serverAddress (node_modules/supertest/lib/test.js:55:18)
      at new Test (node_modules/supertest/lib/test.js:36:12)
      at Object.get (node_modules/supertest/index.js:25:14)
      at Object.<anonymous> (tests/integration/users.test.js:16:47)

  ● /api/v1/users › GET / › should return all users

    TypeError: server.close is not a function

       9 |     });
      10 |     afterEach(() => {
    > 11 |         server.close();
         |                ^
      12 |     });
      13 | 
      14 |     describe("GET /", () => {

      at Object.<anonymous> (tests/integration/users.test.js:11:16)

It looks like it doesn't recognise the "server"-variable as a Server object. Because of that, "server.close()" doesn't work, as a result the server is not shut down. Also the get-request doesn't work. Once again, the server object is not seen as a Server instance.

What am I doing wrong? I hope you can help me out here.


Solution

  • request(server) won't work, you have to use your app instance instead:

    const app = require('../../app'); // Where your app instance is
    
    ...
    
    const res = await request(app).get("/api/v1/users");
    

    Also, try to import your server above all to see if it is recognized as Server object.

    Edit

    Either you want to create http server every test, either you want to do create http server once for all tests and then close it after tests end. Try this :

    // integration test: users.test.js
    const http = require('http');
    const request = require("supertest")
    const app = require('../../app'); // Where your app instance is
    
    describe("/api/v1/users", () => {
      beforeAll(done => {
          server = http.createServer(app);
          server.listen(done);
      });
    
      afterAll(done => {
          server.close(done);
      });
    
      describe("GET /", () => {
            it("should return all users", async (done) => {
                const res = await request(app).get("/api/v1/users");
    
                expect(res.status).toBe(200);
                done();
            });
        });
    });