Why my Supertest test that should be failing is passing? - node.js

I'm starting my project based on this repo:
https://github.com/madhums/node-express-mongoose
The first thing I decided to do was write tests, so I went to the test file, and it looks something like this:
var mongoose = require('mongoose');
var should = require('should');
var request = require('supertest');
var app = require('../server');
var context = describe;
// other stuff you want to include for tests
before(function (done) {
// clear db and other stuff
done();
});
describe('Users', function () {
describe('POST /users', function () {
it('should create a user', function (done) {
request(app)
.post('/users')
.field('name', 'foo')
.field('email', 'foo')
.field('password', 'foo')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res){
console.log(err);
if(err){
console.log("error");
}
// console.log(res);
});
done();
});
});
});
after(function (done) {
// do some stuff
done();
});
I haven't actually created the route, so the test should be failing, and I even do get an error on .end, so any idea why is the test not failing?
My console shows me this when I run npm test
Express app started on port 3000
Users
POST /users
✓ should create a user
[Error: expected "Content-Type" matching /json/, got "text/html; charset=utf-8"]
error
1 passing (653ms)

The problem is that your test is asynchronous, but your code is synchronous. Specifically, the done() line is always called.
You need to move the done() line into the .end block, and add done(err) for the error case, like this:
.end(function(err, res){
if(err){
console.log("error");
done(err);
}
else {
console.log(res);
done();
}
});

Related

why done runs earlier before supertest request on sequelize?

const request = require('supertest');
const app = require('../app');
request(app)
.get("/videos")
.expect(200)
.end(async (err,res) => {
if (err) {
console.error(err);
done();
} else {
console.log(res.body);
done(); // <- this is the question line
}
If I take off the done from the code, the test will print the result, but if I have done there, sequelize still print but there is no res.body print on the console.
Anyone knows why?
I think it's because of the asynchronous nature of javascript.
By default, Jest tests complete once they reach the end of their execution. That means this test will not work as intended:
const request = require('supertest');
const app = require('../app');
request(app)
.get("/videos")
.expect(200)
.end(async (err,res) => {
if (err) {
console.error(err);
// done();
} else {
console.log(res.body);
// done();
}
The problem is that the test will complete as soon as function completes asynchronosly.
Moving to, after putting done()
const request = require('supertest');
const app = require('../app');
request(app)
.get("/videos")
.expect(200)
.end(async (err,res) => {
if (err) {
console.error(err);
done();
} else {
console.log(res.body);
done();
}
And then, in that case, it waits for the done function before finishing the test.
For more details. Please check here.
I hope it helps. Thanks
Try to remove async from .end(async (err,res) => { line so the callback is not considered asynchronous anymore.

Mocha tests not failing when they should

I'm trying to test my routes file, and mocha is returning success for all of my expects, even though I've coded a couple that should absolutely fail. I added a 2+2 = 5 test just to make sure something would fail. I have done() in my assertion blocks.
I'm using a MEAN stack, and I tried to test the node files with jasmine, since I'm already using that to test the Angular files, but got tons of crazy errors, so I threw all that out and decided to give mocha a try instead.
Results:
Routes
1) makes sure something fails
GET /
√ returns status code 200
GET /nonexistent
√ returns status code 400
GET /api/todos
√ returns status code 200
√ returns a list of todos
Test file
// test/routes.spec.js
var request = require('request');
var expect = require('chai').expect;
describe('Routes', function() {
var base_url = "http://localhost:8080/"
// does fail as expected
it("makes sure something fails", function () {
expect(2 + 2).to.equal(5);
});
describe("GET /", function() {
it("returns status code 200", function() {
request(base_url, function(error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
});
//should fail
describe("GET /nonexistent", function() {
it("returns status code 400", function () {
request(base_url + "/nonexistent", function (error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
});
describe("GET /api/todos", function() {
it("returns status code 200", function() {
request(base_url + "/api/todos", function(error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
//should fail
it("returns a list of todos", function() {
request(base_url + "/api/todos", function(error, response, body) {
console.log(body);
expect(body).to.equal("abcd");
done();
});
});
});
});
Routes file:
// app/routes.js
var Todo = require('./models/todo');
module.exports = function(app) {
// api ---------------------------------------------
// get all todos
app.get('/api/todos', function (req, res) {
Todo.find(function (err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
// create todo and send back all todos after creation
app.post('/api/todos', function (req, res) {
Todo.create({
text: req.body.text,
done: false
}, function (err, todo) {
if (err)
res.send(err);
Todo.find(function (err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
});
// delete a todo
app.delete('/api/todos/:todo_id', function (req, res) {
Todo.remove({
_id: req.params.todo_id
}, function (err, todo) {
if (err)
res.send(err);
Todo.find(function (err, todos) {
if (err)
res.send(err)
res.json(todos);
})
})
})
// application --------------------------------------
app.get('*', function (req, res) {
res.sendFile(__dirname + '/public/index.html');
});
};
You want to use the done callback but none of your tests declare it in the parameters of the callbacks passed to it. Your first test, for instance, should be:
it("returns status code 200", function (done) { // <== Add parameter here!
request(base_url, function(error, response, body) {
expect(response.statusCode).to.equal(200);
done();
});
});
Without the parameter, Mocha considers the test to be synchronous. So it does not wait for request to call its callback, and ends right away. The fact that done is undefined does not lead to an error because the JavaScript interpreter does not get to done() before Mocha deems the tests over.
I'm a JavaScript novice and had to change my code from
it('getReports', () => {
getReports()
.then((res) => {
assert.equal(200, res.statusCode);
});
});
to
it('getReports', () => getReports()
.then((res) => {
assert.equal(200, res.statusCode);
}));
i.e. Had to remove the first set of curly brackets.
After this the Mocha tests reported an error.
Starting with Node 8 you can use the native async/await approach for requests and testing.
First use request-promise or request-promise-native instead request.
const request = require('request-promise-native');
Tests with async/await:
// testing success results - any error will fail the test
it('Returns status code 200', async () => {
const response = await request(base_url);
expect(response.statusCode).to.equal(200);
});
// testing for a particular error
it('Testing a particular error is thrown', async () => {
let error;
try {
await request(base_url);
} catch (err) {
error = err;
}
expect(error).to.be.ok;
expect(error.message).to.equal('Expected error message');
});
In my case running test files with the below command solved the problem.
node --unhandled-rejections=strict node_modules/.bin/mocha --require #babel/register --require babel-polyfill test/**/*.test.js

500 equals 400 passes in a mocha test?

Why does the following test pass?
"use strict";
const
path = require('path'),
Dexter = require('../src/Dexter.js'),
chai = require('chai'),
chaiHttp = require('chai-http'),
expect = chai.expect,
dexterServer = new Dexter(path.resolve(__dirname, 'test/data/sample.har'));
chai.use(chaiHttp);
describe('Rest API', () => {
before(() => {
dexterServer.startUp();
});
it('should\'ve started the server', function () {
chai.request('http://127.0.0.1:1121')
.get('/')
.end(function(err, response){
console.log(response.status);
expect(500).to.equal(400);// This passes? What?
done();
});
});
after(() => {
dexterServer.tearDown();
});
});
When I do a console.log of the response.status, I see a 200. But when I do
expect(response.status).to.equal(400);//response.status is an int
passes the test!
What am I doing wrong?
You forgot to pass done callback. it was treated as sync with 0 assumptions.
it('should\'ve started the server', function (done) {
chai.request('http://127.0.0.1:1121')
.get('/')
.end(function(err, response){
console.log(response.status);
expect(500).to.equal(400);// This passes? What?
done();
});
});
You have to pass done in it, before and after statement to maintain the asynchronous flow.
describe('Rest API', (done) => {
before(() => {
dexterServer.startUp();
});
it('should\'ve started the server', function (done) {
chai.request('http://127.0.0.1:1121')
.get('/')
.end(function(err, response){
console.log(response.status);
expect(500).to.equal(400);// This passes? What?
done();
});
});
after((done) => {
dexterServer.tearDown();
});
});
Mocha supports promises so you could use the fact that chai-http produces promises and just return the promise:
it('should\'ve started the server', function () {
// Return the promise.
return chai.request('http://127.0.0.1:1121')
.get('/')
// Use .then instead of .end.
.then(function(response){
console.log(response.status);
expect(500).to.equal(400);
});
});
If you need to do special processing of errors, you could have a .catch too. Otherwise, you can let Mocha handle any error as a failure.

Grab specific response properties from SuperTest

I want to be able to grab some response properties and throw them into a variable at times with SuperTest. How can I do this? I don't see the docs doing anything but assertions on the response.
for example I'd like to do something like this:
var statusCode = request(app).get(uri).header.statusCode;
I'd like to do something like this. Because sometimes I like to split out the asserts into seperate Mocha.js it() tests due to the fact I'm doing BDD and so the 'Thens' in this case are based on the expected response parts so each test is checking for a certain state coming back in a response.
for example I'd like to do this with supertest:
var response = request(app).get(uri);
it('status code returned is 204, function(){
response.status.should.be....you get the idea
};
it('data is a JSON object array', function(){
};
Here is an example how you can accomplish what you want:
server file app.js:
var express = require('express');
var app = express();
var port = 4040;
var items = [{name: 'iphone'}, {name: 'android'}];
app.get('/api/items', function(req, res) {
res.status(200).send({items: items});
});
app.listen(port, function() {
console.log('server up and running at %s:%s', app.hostname, port);
});
module.exports = app;
test.js:
var request = require('supertest');
var app = require('./app.js');
var assert = require('assert');
describe('Test API', function() {
it('should return 200 status code', function(done) {
request(app)
.get('/api/items')
.end(function(err, response) {
if (err) { return done(err); }
assert.equal(response.status, 200);
done();
});
});
it('should return an array object of items', function(done) {
request(app)
.get('/api/items')
.end(function(err, response) {
if (err) { return done(err); }
var items = response.body.items;
assert.equal(Array.isArray(items), true);
done();
});
});
it('should return a JSON string of items', function(done) {
request(app)
.get('/api/items')
.end(function(err, response) {
if (err) { return done(err); }
try {
JSON.parse(response.text);
done();
} catch(e) {
done(e);
}
});
});
});
You can see some examples here on the superagent github library since supertest is based on superagent library.

Supertest + Express won't fail

This is more or less a duplicate of supertest test express middleware
but after a year, I figured I'd start a new question.
var express = require('express');
var request = require('supertest');
var app1 = express();
app1.get('/myapp', function (req, res) {
res.send(200, { name: 'myapp' });
});
request = request(app1);
it('should fail', function () {
request
.get('/hahahahahahahaha')
.expect(123);
});
As far as I can tell, that will always erroneously pass. The fact that the path is wrong and is expecting a different status code doesn't matter.
And - more generically (without Express), it looks like this always passes, also:
it('should fail', function () {
request('http://thisdoesnotexist.mydomain')
.get()
.expect(200);
});
This doesn't work either:
it('should fail', function () {
request('http://thisdoesnotexist.mydomain')
.get()
.expect(200)
.end(function (err, res) {
if (err) {
throw err;
}
});
});
Any thought as to why this happens, or how to actually test such a scenario?
With supertest you need to terminate your chain somehow.
expect will take a finished callback as the second parameter, and you can use the build in mocha callback for this. Like so:
describe('potato', function() {
it('should fail', function(done) {
request
.get('/hahahahahahahaha')
.expect(123, done);
});
});
Specifying a done option like this will instruct mocha to wait until it's heard back from you before proceeding to the next test.
The difference is the parameter: done
describe('XXX', function() {
it('XXX', function() {
// always passing
})
})
describe('YYY', function(done) {
it('YYY', function() {
// always passing
})
})
describe('ZZZ', function() {
it('ZZZ', function(done) {
// normal
})
})

Resources