Search code examples
node.jsexpressjestjstddsupertest

Node.js with Express, Jest and SuperTest never fail


I'm new in Jest and TDD. Please, help me! I'm using supertest to request the API, but even with the server turned off, the tests never fail. I've tried use return or async await and it not solved

I have the following structure at Node.js project:

nodemodules
src
    controllers
        users-controller.js
    index.js
    routes.js
    server.js
test
    user.test.js
package.json

package.json:

"scripts": {
    "test": "jest",
    "lint": "eslint src/** test/** --fix",
    "start": "node src/server.js",
    "jest-watch": "jest --watch"
},
"devDependencies": {
    "eslint": "^6.8.0",
    "jest": "^25.3.0",
    "supertest": "^4.0.2"
},
"dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1"
}

src/server.js:

const app = require('./index')

app.listen(3001, () => {
    console.log('Server running on port 3001')
})

src/index.js:

const express = require('express')
const routes = require('./routes')
const app = express()

app.use(express.json())
app.use(routes)

module.exports = app

src/routes.js

const routes = require('express').Router()
const UserController = require('./controllers/users-controller')

routes.get('/', (req, res) => { res.status(200).send() })
routes.get('/users', UserController.findAll)
routes.post('/users', UserController.create)

module.exports = routes

src/controllers/user-controller.js

module.exports = {
    findAll(req, res) {
        const users = [
            { name: 'John Doe', mail: '[email protected]' }
        ]
        return res.status(200).json(users)
    },

    create(req, res) {
        return res.status(201).json(req.body)
    }
}}

test/user.test.js:

const request = require('supertest')
const app = require('../src/index')


test('Should list users', () => {
    return request(app).get('/users')
        .then(res => {
            expect(res.status).toBe(200)
            expect(res.body).toHaveLength(1)
            expect(res.body[0]).toHaveProperty('name', 'John Doe')
        })
})

test('Should insert a user', async () => {
    await request(app).post('/users')
        .send({ name: 'Walter Mitty', email: '[email protected]' })
        .then(res => {
            expect(res.status).toBe(201)
            expect(res.body.name).toBe('Walter Mitty')
        })
})

And the result is always the same:

PASS  test / user.test.js
✓ Should list users. (16ms)
✓ Should insert a user. (13ms)

Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.456s, estimated 1s
Ran all test suites related to changed files.

Watch Usage: Press w to show more.

Solution

  • Oh I see what the problem is, although isn't really a problem, is the normal behavior of Jest and Supertest. I'll explain to you. If Express app isn't listening, it doesn't affect the Jest or Supertest behavior. Why? Just because the app that you pass to supertest request(app) runs an independent process of the app, and when the tests are finished, that app finishes too. In other words, Supertest runs the Express app on a different process, do the tests, and finishes that process.

    This test will fail if you expect another response code, for example 400. The following test should fail for sure:

    test('Should list users', async () => {
        // First assertion
        const response = await request(app)
            .get('/users')
            .send()
            .expect(400) // This will make fail, because the app instance running for test, is working just fine and will return 200, if you expect a different code, so the test will fail
    
        // Second assertion
        expect(response.body[0]).toHaveProperty('name', 'John Doe')
    })
    

    So that is the normal behavior of Jest and Supertest. It runs the Express app, just for running the tests, in another independent process. It doesn't matter if your Express main process is still running or if it's stopped.