Home > database >  simple express app works when run, fails testing
simple express app works when run, fails testing

Time:03-02

Using express, I am supposed to write an app that gets track info from a JSON file that was provided:

  1. get all tracks
  2. get specific track by id
  3. get a list of tracks ordered by track name or duration (depends on the sortBy query string parameter)

The app works fine when I fiddle with it using Postman, but when writing tests using Jest and Supertest they all fail.

app

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const logger = require('morgan');

const tracksRouter = require('./routes/routes-tracks');

const app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.use('/tracks', tracksRouter);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.send(err.message);
});

module.exports = app;  

routes

const express = require('express');
const router = express.Router();
const tracks = require('../controllers/controller');


router.get('/sorted', function (req, res) {
    try {
        const result = tracks.getSorted(req.query.sortBy);
        res.status(200).send(result);
        res.end();
    } catch (error) {
        res.status(500).send(error);
        res.end();
    }
});

router.get('/:id', function (req, res) {
    try {
        const result = tracks.getById(req.params.id);
        res.status(200).send(result);
        res.end();
    } catch (error) {
        res.status(500).send(error);
        res.end();
    }
});

router.get('/', function (req, res) {
    try {
        const result = tracks.getAll();
        res.status(200).send(result);
        res.end();
    } catch (error) {
        res.status(500).send(error);
        res.end();
    }
});


module.exports = router;

routes test

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


describe('GET /tracks', () => {

    it('GET /tracks/sorted?sortBy=duration should return array of tracks sorted by duration', () => {
        const res = request(app).get('/tracks/sorted?sortBy=duration');

        expect(res.statusCode).toEqual(200);
        expect(res.body[0].id).toEqual(ID_EDITED);
    });

    it('GET /tracks/sorted?sortBy=name should return array of tracks sorted by name', () => {
        const res = request(app).get('/tracks/sorted?sortBy=name');

        expect(res.statusCode).toEqual(200);
        expect(res.body[0].artist.name).toEqual(ARTIST_EDITED);
    });

    it('GET /tracks:id should return track with given id', () => {
        const res = request(app).get('/tracks/ID_EDITED');

        expect(res.statusCode).toEqual(200);
        expect(res.body.id).toEqual(ID_EDITED);
        expect(res.body.title).toEqual(SONGNAME_EDITED);
    });

    it('GET /tracks should return an array of all tracks', () => {
        const res = request(app).get('/tracks');
        expect(res.statusCode).toEqual(200);
        expect(res.body[0]).toHaveProperty('duration');
    });
});

controller

const database = require('../tools/db-import');
const trackList = database.getTrackList();

function getAll() {
    try {
        return trackList;
    } catch (error) {
        return error;
    }
}

function getById(trackId) {
    try {
        return trackList.find((item) => item.id == trackId);
    } catch (error) {
        return error;
    }
}

function getSorted(sortParameter) {
    try {
        if (sortParameter == 'duration') {
            return trackList.sort((a, b) => (a.duration > b.duration) ? 1 : -1);
        } else if (sortParameter == 'name') {
            return trackList.sort((a, b) => (a.artist.name > b.artist.name) ? 1 : -1);
        }
        else {
            return 'Wrong input.'
        }
    } catch (error) {
        return error;
    }
}

module.exports.getAll = getAll;
module.exports.getById = getById;
module.exports.getSorted = getSorted;

model

const tracks = require('../models/tracks');

function getAll() {
    try {
        return tracks.getAll();
    } catch (error) {
        return error;
    }
}

function getById(id) {
    try {
        return tracks.getById(id);
    } catch (error) {
        return error;
    }
}

function getSorted(sortParameter) {
    try {
        return tracks.getSorted(sortParameter);
    } catch (error) {
        return error;
    }
}

module.exports.getAll = getAll;
module.exports.getById = getById;
module.exports.getSorted = getSorted;

database import

const data = require('../database/JSON_EDITED.json');

function getTrackList() {
    return data.tracks.data;
}

module.exports.getTrackList = getTrackList;

example of jest's failure message:

    expect(received).toEqual(expected) // deep equality

    Expected: 200
    Received: undefined

       8 |         const res = request(app).get('/tracks/sorted?sortBy=duration');
       9 |
    > 10 |         expect(res.statusCode).toEqual(200);
         |                                ^
      11 |         expect(res.body[0].id).toEqual(ID_EDITED);
      12 |     });
      13 |

      at Object.<anonymous> (routes/__test__/routes-tracks.test.js:10:32)

CodePudding user response:

If you take a look at the supertest documentation, you can see that the request function is asynchronous and returns a promise.

They provide different syntax but I would personally used the async/await one:

it('GET /tracks/sorted?sortBy=duration should return array of tracks sorted by duration', async () => {
  const res = await request(app).get('/tracks/sorted?sortBy=duration');

  expect(res.statusCode).toEqual(200);
  expect(res.body[0].id).toEqual(ID_EDITED);
});

But you could also use a then callback:

it('GET /tracks/sorted?sortBy=duration should return array of tracks sorted by duration', () => {
  return request(app)
    .get('/tracks/sorted?sortBy=duration')
    .then((res) => {
      expect(res.statusCode).toEqual(200);
      expect(res.body[0].id).toEqual(ID_EDITED);
    });
});

And finally, you could use Jest's async matchers (not ideal for this use case, only partial check is done here):

it('GET /tracks/sorted?sortBy=duration should return array of tracks sorted by duration', () => {
  expect(request(app).get('/tracks/sorted?sortBy=duration')).resolves.toHaveProperty('statusCode', 200);
});
  • Related