NodeJS: How to test middleware making external call - node.js

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

Related

How to mock req.session in express while testing API with jest and supertest?

What I'm trying to do is to test a REST API endpoint that validates requests with req.session (set by express-session).
Here are snippets of my code:
middleware.ts
export const validateSecurityCode = (req, res, next) => {
// How do I mock/spy on this "req.session.securityCode"?
if (req.session.securityCode !== req.body.securityCode) {
throw new BadRequestError('Wrong security code was provided');
}
return next();
};
api/item.ts
router.post('/item', [validateSecurityCode], async (req, res) => {
await createItem(req.body);
res.send({ message: 'DONE' });
});
As you can see, if req.session.securityCode !== req.body.securityCode, this endpoint has to throw an error.
Is it possible to mock/spy on req.session while testing the endpoint with jest and supurtest?
Below is what I'd like to achieve:
it('should pass security code check', async () => {
// " MOCK_REQ_SESSION('valid-code')" will ensure that "req.session.securityCode" will get 'valid-code'
validateSecurityCode = MOCK_REQ_SESSION('valid-code');
const response = await request(app).post('/api/item').send({
securityCode: 'valid-code',
// other request body values
});
expect(response.statusCode).toEqual(200);
});

Nodejs best practice encapsulating send middleware

Ok so I am currently learning more node.js and decided to try out some basic middleware in a small api I created. I was wondering how I would wrap a successfull request. This is my approach.
Example Controller
exports.getTask = async function (req, res, next) {
try {
const task = await db.Task.findOne(
{
where: {
id: req.params.taskId,
userId: req.params.userId
}
});
if (task) {
req.locals.data = task;
res.status(httpStatus.OK);
next();
}
res.status(httpStatus.NOT_FOUND);
next();
} catch (err) {
next(err);
}
};
Middleware
exports.success = function(req, res, next) {
const success = res.statusCode < 400;
const successResponse = {
timestamp: new Date().toUTCString(),
success: success,
status: res.statusCode
};
if (success) {
successResponse.data = req.locals.data;
}
res.send(successResponse);
next();
};
I dont think its very good having to set req.locals.data for every requst and then calling next res.status(status) maybe I just approached the situation the wrong way?
How could you make this better?
In this case, probably using the express middleware concept (calling next()) will be an overkill.
I'd approach this by creating an abstraction for the success path. Consider something like this:
const resWithSuccess = (req, res, data) => {
res.json({
data: data,
timestamp: new Date().toUTCString(),
// success: res.statusCode < 400, // --> actually you don't need this,
// since it will always be true
// status: res.statusCode // --> or whatever else "meta" info you need
});
};
Then, as soon as you need to respond with success, go for it:
exports.getTask = async function (req, res, next) {
// .... bla bla
if (task) {
resWithSuccess(tank);
}
};
PS: ... and you can use the express middleware concept (calling next()) for the error path.

How to test response data from Express in Jest

I'm writing unit tests for separate middleware functions in Node/Express using Jest.
A simple example of the middleware:
function sendSomeStuff(req, res, next) {
try {
const data = {'some-prop':'some-value'};
res.json(data);
next();
} catch (err) {
next(err);
}
}
And a sample of my test suite:
const httpMocks = require('node-mocks-http');
const { sendSomeStuff } = require('/some/path/to/middleware');
describe('sendSomeStuff', () => {
test('should send some stuff', () => {
const request = httpMocks.createRequest({
method: 'GET',
url: '/some/url'
});
let response = httpMocks.createResponse();
sendSomeStuff(request, response, (err) => {
expect(err).toBeFalsy();
// How to 'capture' what is sent as JSON in the function?
});
});
});
I have to provide a callback to populate the next parameter, which is called in the function. Normally, this would 'find the next matching pattern', and pass the req and res objects to that middleware. However, how can I do this in a test set-up? I need to verify the JSON from the response.
I don't want to touch the middleware itself, it should be contained in the test environment.
Am I missing something here?
Found a fix!
Leaving this here for someone else who might struggle with the same.
When returning data using res.send(), res.json() or something similar, the response object (from const response = httpMocks.createResponse();)
itself is updated. The data can be collected using res._getData():
const httpMocks = require('node-mocks-http');
const { sendSomeStuff } = require('/some/path/to/middleware');
describe('sendSomeStuff', () => {
test('should send some stuff', () => {
const request = httpMocks.createRequest({
method: 'GET',
url: '/some/url'
});
const response = httpMocks.createResponse();
sendSomeStuff(request, response, (err) => {
expect(err).toBeFalsy();
});
const { property } = JSON.parse(response._getData());
expect(property).toBe('someValue');
});
});
});
I did a different way by utilising jest.fn(). For example:
if you wanna test res.json({ status: YOUR_RETURNED_STATUS }).status(200);
const res = {};
res.json = jest.fn(resObj => ({
status: jest.fn(status => ({ res: { ...resObj, statusCode: status }
})),
}));
Basically, I mock the res chain methods(json and status).
That way you can do expect(YOUR_TEST_FUNCTION_CALL).toEqual({ res: { status: 'successful', statusCode: 200 }}); if your response structure is like that.

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

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.

Resources