Supertest + Express won't fail - node.js

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
})
})

Related

Correct way to unit test Express Middleware [duplicate]

This question already has answers here:
express middleware testing mocha chai
(2 answers)
Closed 6 years ago.
I have a piece of Express middleware that is set to check for a valid Content-Type header in all of my POST requests that hit my server, the code for this middleware is below:
import * as STRINGS from "../Common/strings";
function ContentTypeValidator(req, res, next) {
let contentHeader = req.get("content-type");
if(!contentHeader) {
res.status(400).send(STRINGS.ERROR_CONTENT_TYPE_MISSING);
} else {
if(contentHeader.toLowerCase() !== "application/json") {
res.status(415).send(STRINGS.ERROR_CONTENT_TYPE_UNSUPPORTED);
} else {
next();
}
}
}
export default ContentTypeValidator;
I am using mocha, chai and node-mocks-http for my TDD and my question surrounds the tests when next() will not be called as res.send() will handle the ending of this request for me.
it("Should return 200 for valid Content-Type header", (done) => {
req = nodeMocks.createRequest({
headers: {
"Content-Type": "application/json"
}
});
ContentTypeValidator(req, res, (err) => {
res.statusCode.should.equal(200);
expect(err).to.be.undefined;
done();
});
});
it("Should return 400 if Content-Type header is missing", (done) => {
ContentTypeValidator(req, res, () => {});
res.statusCode.should.equal(400);
res._getData().should.equal("Content-Type header missing");
done();
});
In the first test above, I am expecting this to pass, so I pass in a function to act as the next() function and this test passes. In the second test, I am expecting this to fail so if I pass in a function then mocah complains that the test has exceeded 2000ms as the callback function is never called, which is to be expected since res.send() is handling it in this instance.
Is the way I've written the second test correct when it comes to unit testing Express middleware like this or is there a better/more advisable way to do this?
EDIT: So just to clarify, I am focused on wanting to test the middlewear when the next callback will NOT be called, the question I'm apparently duplicating is looking at using sinon to check if next is called. I am looking to see how to unit test when the callback function will NOT be called.
Check out this answer
https://stackoverflow.com/a/34517121/4996928
var expect = require('chai').expect;
var sinon = require('sinon');
var middleware = function logMatchingUrls(pattern) {
return function (req, res, next) {
if (pattern.test(req.url)) {
console.log('request url', req.url);
req.didSomething = true;
}
next();
}
}
describe('my middleware', function() {
describe('request handler creation', function() {
var mw;
beforeEach(function() {
mw = middleware(/./);
});
it('should return a function()', function() {
expect(mw).to.be.a.Function;
});
it('should accept three arguments', function() {
expect(mw.length).to.equal(3);
});
});
describe('request handler calling', function() {
it('should call next() once', function() {
var mw = middleware(/./);
var nextSpy = sinon.spy();
mw({}, {}, nextSpy);
expect(nextSpy.calledOnce).to.be.true;
});
});
describe('pattern testing', function() {
...
});
});

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.

Why my Supertest test that should be failing is passing?

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();
}
});

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.

Mock fs.readdir for testing

I'm trying to mock the function fs.readdir for my tests.
At first I've tried to use sinon because this is a very good framework for this, but is hasn't worked.
stub(fs, 'readdir').yieldsTo('callback', { error: null, files: ['index.md', 'page1.md', 'page2.md'] });
My second attempt was to mock the function with a self-replaced function. But it also doesn't works.
beforeEach(function () {
original = fs.readdir;
fs.readdir = function (path, callback) {
callback(null, ['/content/index.md', '/content/page1.md', '/content/page2.md']);
};
});
afterEach(function () {
fs.readdir = original;
});
Can anybody tell me why both doesn't works? Thanks!
Update - This also doesn't works:
sandbox.stub(fs, 'readdir', function (path, callback) {
callback(null, ['index.md', 'page1.md', 'page2.md']);
});
Update2:
My last attempt to mock the readdir function is working, when I'm trying to call this function directly in my test. But not when I'm calling the mocked function in another module.
I've found the reason for my problem. I've created the mock in my test class tried to test my rest api with supertest. The problem was that the test was executed in another process as the process in that my webserver runs. I've created the express-app in my test class and the test is now green.
this is test
describe('When user wants to list all existing pages', function () {
var sandbox;
var app = express();
beforeEach(function (done) {
sandbox = sinon.sandbox.create(); // #deprecated — Since 5.0, use sinon.createSandbox instead
app.get('/api/pages', pagesRoute);
done();
});
afterEach(function (done) {
sandbox.restore();
done();
});
it('should return a list of the pages with their titles except the index page', function (done) {
sandbox.stub(fs, 'readdir', function (path, callback) {
callback(null, ['index.md', 'page1.md', 'page2.md']);
});
request(app).get('/api/pages')
.expect('Content-Type', "application/json")
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var pages = res.body;
should.exists(pages);
pages.length.should.equal(2);
done();
});
});
});

Resources