Using express, I am supposed to write an app that gets track info from a JSON file that was provided:
- get all tracks
- get specific track by id
- 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);
});