Correct way to unit test Express Middleware [duplicate] - node.js

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

Related

Unit test promise result inside Event Listener in Node

I have the following code that I want to test.
emitter.on("request", function(req, res) {
mock_finder.getMockedResponse().then((mockedResponse) => {
res.end(mockedResponse);
});
});
Then, I have this unit test.
it("should return mocked response", () => {
// given
const mockedResponse = {
success: true
};
mock_finder.getMockedResponse.mockImplementation(() => Promise.resolve(mockedResponse));
const res = {
end: jest.fn()
}
// when
emitter.emit('request', req, res);
// then
expect(res.end).toHaveBeenCalledWith(mockedResponse);
});
This test is not working because res.end(mockedResponse); is executed after the test finishes.
How can I test the promise response after an event is called?
Given there's no real async code going on here, you can verify the result on the next tick:
if('should return mocked response', done => {
...
emitter.emit('request', req, res);
process.nextTick(() => {
expect(res.end).toHaveBeenCalledWith(mockedResponse);
done()
});
})

NodeJS: How to test middleware making external call

I have an authentication middleware I will like to test, the middleware makes an external call to an authentication service and based on the returned statusCode either calls the next middleware/controller or it returns a 401 status. Something like what I have below.
var auth = function (req, res, next) {
needle.get('http://route-auth-service.com', options, function (err, reply) {
if (reply.statusCode === 200) {
next();
} else {
res.statusCode(401)
}
})
}
I use SinonJS, nock, and node-mocks-http for testing and my simple test is as below.
// require all the packages and auth middleware
it('should login user, function (done) {
res = httpMocks.createResponse();
req = httpMocks.createRequest({
url: '/api',
cookies: {
'session': true
}
});
nock('http://route-auth-service.com')
.get('/')
.reply(200);
var next = sinon.spy()
auth(res, req, next);
next.called.should.equal(true); // Fails returns false instead
done();
});
The test always fails and returns false, I feel that the reason is because the needle call is asynchronous, and before the call returns the assertion part is reached. I have been working on this all day, I need help please.
you need to split the test setup away from the assertion
// this may be "beforeEach"
// depends on what testing framework you're using
before(function(done){
res = httpMocks.createResponse();
req = httpMocks.createRequest({
url: '/api',
cookies: {
'session': true
}
});
nock('http://route-auth-service.com').get('/').reply(200);
var next = sinon.spy();
auth(res, req, function() {
next();
done();
});
});
it('should login user', function () {
next.called.should.equal(true); // Fails returns false instead
});

express middleware testing mocha chai

Is there a way to test those kind of middleware in express:
module.exports = function logMatchingUrls(pattern) {
return function (req, res, next) {
if (pattern.test(req.url)) {
console.log('request url', req.url);
req.didSomething = true;
}
next();
}
}
The only middleware testing i found was:
module.exports = function(request, response, next) {
/*
* Do something to REQUEST or RESPONSE
**/
if (!request.didSomething) {
console.log("dsdsd");
request.didSomething = true;
next();
} else {
// Something went wrong, throw and error
var error = new Error();
error.message = 'Error doing what this does'
next(error);
}
};
describe('Middleware test', function(){
context('Valid arguments are passed', function() {
beforeEach(function(done) {
/*
* before each test, reset the REQUEST and RESPONSE variables
* to be send into the middle ware
**/
requests = httpMocks.createRequest({
method: 'GET',
url: '/css/main.css',
query: {
myid: '312'
}
});
responses = httpMocks.createResponse();
done(); // call done so that the next test can run
});
it('does something', function(done) {
/*
* Middleware expects to be passed 3 arguments: request, response, and next.
* We are going to be manually passing REQUEST and RESPONSE into the middleware
* and create an function callback for next in which we run our tests
**/
middleware(responses, responses, function next(error) {
/*
* Usually, we do not pass anything into next except for errors, so because
* in this test we are passing valid data in REQUEST we should not get an
* error to be passed in.
**/
if (error) { throw new Error('Expected not to receive an error'); }
// Other Tests Against request and response
if (!responses.didSomething) { throw new Error('Expected something to be done'); }
done(); // call done so we can run the next test
}); // close middleware
}); // close it
}); // close context
}); // close describe
This work well with the simple middleware (it like testing basic function with callback) provided above but with more complex middleware i cannot get it work. Is it possible to test this kind of middleware?
Here's a simple setup that you could use, using chai and sinon:
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() {
...
});
});
From there, you can add more elaborate tests for the pattern matching, etc. Since you're only using req.url, you don't have to mock an entire Request object (as created by Express) and you can just use a simple object with a url property.
I used node-mocks-http to unit test my middleware. Here's my code:
function responseMiddleware(req, res, next) {
res.sendResponse = (...args) => {
//<==== Code removed from here
};
next();
}
And in my spec file I did it like this:
var expect = require('chai').expect;
var sinon = require('sinon');
var responseMiddleware = require('./response');
var httpMocks = require('node-mocks-http');
describe('request handler calling', function() {
it('should call next() once', function() {
var nextSpy = sinon.spy();
responseMiddleware({}, {}, nextSpy);
expect(nextSpy.calledOnce).to.be.true;
});
it('should add sendResponse key', function() {
var nextSpy = sinon.spy();
var req = httpMocks.createRequest();
var res = httpMocks.createResponse();
responseMiddleware(req, res, nextSpy);
expect(nextSpy.calledOnce).to.be.true;
responseMiddleware(req, res, () => {
expect(res).to.have.property('sendResponse');
})
});
});
If you are using async calls then you can use await and then call done() after that.

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

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