Testing functions that contains async calls - node.js

I'm trying to test the get function:
exports.get = function(req, res) {
Subscriptions
.find(req.params.id)
.success(function(subscription) {
if (subscription) {
res.json({message: "Success"}, 200);
} else {
res.json({message: "Not found"}, 404);
}
})
.error(function(error) {
res.json({message: "Internal server error"}, 500);
});
};
Specifically, I don't really care if it hits the database, I only want to test the scenarios where the success and error events occur. I'm using sequelize.js as my orm to handle the database. I've gotten a test up and running, but its a bit nasty, with the timeout. Is there a better way of doing this? Here's the test I've written so far:
var express = require('express')
, sinon = require('sinon')
, subscription = require('app/controllers/subscriptions')
, Subscriptions = require('app/models/subscriptions')
;
describe('subscription controller', function() {
beforeEach(function() {
this.mockResponse = sinon.mock(express.response);
});
afterEach(function() {
this.mockResponse.restore();
});
describe('GET /subscriptions/:id', function() {
it('should return a json response', function(done) {
var request = {
params: {
id: 'identifier'
}
};
var expectedResponse = {
subscriptions_uri : "/subscription/identifier"
};
this.mockResponse
.expects('json')
.once()
.withArgs(expectedResponse);
subscription.get(request, express.response);
setTimeout(function() {
done();
}, 500);
});
});
});

I decided to use the supertest library, which made testing my controller incredibly easy:
var express = require('express')
, subscription = require('app/controllers/subscriptions')
, request = require('supertest')
, app = express()
;
describe('subscription controller', function() {
describe('GET /subscriptions/:id', function() {
it('should return a json response', function(done) {
var expectedBody = {
subscriptions_uri : "/subscription/identifier"
};
request(app)
.get('/subscriptions/identifier')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(expectedBody)
.expect(200, done);
});
});
});

Related

How to test requests that require the user to be authenticated in Express

How can you test requests that require the user to be authenticated? I'm using local passport.js in my express app. I'm testing using Jest and Supertest. I've looked up countless posts, I tried supertest-session and that doesn't seem to work either. All of these posts are from almost 10 years ago so I'm not sure if they're still valid.
Here is the last thing that I've tried:
const request = require('supertest');
const app = require('../app');
const { pool } = require('../dbConfig');
let testAccount = { email: 'loginTestUser#gmail.com', password: '123456' }
const loginUrl = '/users/login';
const createRoomUrl = '/rooms/create'
const successfulRoomCreateUrl = '/users/dashboard';
const successfulLoginUrl = '/users/dashboard';
const failedRoomCreateUrl = '/';
afterAll(async () => {
pool.end();
});
describe('Room CRUD: Create | POST /rooms/create', () => {
describe('Given the user is Authenticated', () => {
let token;
beforeEach( async () => {
const response = await
request(app).post(loginUrl).type('form').send(testAccount);
token = { access_token: response.body.token }
});
test(`should get statusCode 200 if user is logged in`, async () => {
const createRoomResponse = await request(app).get(createRoomUrl).query(token);
// 302 since the user doesn't stay logged in
expect(createRoomResponse.statusCode).toEqual(200);
});
});
});
Here is what I tried with supertest-session and it also doesn't work:
const session = require('supertest-session');
const myApp = require('../app');
let testAccount = { email: 'loginTestUser#gmail.com', password: '123456' }
var testSession = null;
beforeEach(function () {
testSession = session(myApp);
});
it('should fail accessing a restricted page', function (done) {
testSession.get('/rooms/create')
.expect(302)
.end(done)
});
it('should sign in', function (done) {
testSession.post('/users/login')
.send(testAccount)
.expect(302) // get redirected to dashboard
.end(done);
});
describe('after authenticating session', function () {
var authenticatedSession;
beforeEach(function (done) {
testSession.post('/users/login')
.send(testAccount)
.expect(302) // get redirected to /users/dashboard
.end(function (err) {
if (err) return done(err);
authenticatedSession = testSession;
return done();
});
});
it('should get a restricted page', function (done) {
authenticatedSession.get('/rooms/create')
.expect(200) // <------ still get a 302 (redirect if user !logged
.end(done)
});
});
Turns out all I needed to do was append .type('form') like so:
beforeEach(function (done) {
testSession.post('/users/login').type('form')
.send(testAccount)
.expect(302) // get redirected to /users/dashboard
.end(function (err) {
if (err) return done(err);
authenticatedSession = testSession;
return done();
});
});

Mocha with chai and supertest: expected undefined to equal

I wrote the unit tests:
var app = require('../server');
var chai = require('chai');
var supertest = require("supertest")(app);
var GoogleUrl = require('google-url');
var config = require('../config');
var expect = chai.expect;
describe('Urls Tests', function () {
var url = {
author : 'Alexey',
description : 'grrggr',
full_url : 'https://github.com',
date : '30-06-2017',
time : '18:21:27',
count_click : 0,
list_tags : [
'Sport',
'Football'
]
};
var token;
beforeEach(function (done) {
agent
.post('http://localhost:8000/auth/login')
.send({email: 'Keane95#yandex.ru', password: '123456'})
.end(function (err, res) {
if (err) {
return done(err);
}
expect(res.body.userData).to.have.property('token');
token = res.body.userData.token;
done();
});
});
it('should create a url', function(done) {
var googleUrl = new GoogleUrl({
'key': config.get('google_key')
});
googleUrl.shorten(url.full_url, function (err, shortUrl) {
url.short_url = shortUrl;
supertest
.post('/urls/create')
.send(url)
.expect(401)
.end(function (err, res) {
if (err) return done(err);
expect(res.body.author).to.equal('Alexey');
url = res.body;
done();
});
});
});
it('should modify a url by id', function(done) {
url.description = 'Good description';
url.list_tags.push('Liverpool');
supertest
.put('/urls/' + url._id)
.send(url)
.expect(401)
.end(function(err, res) {
if (err) return done(err);
expect(res.body.description).to.equal('Good description');
expect(res.body.list_tags[2]).to.equal('Liverpool');
done();
});
});
it('should modify a count of clicks', function(done) {
url.count_click++;
supertest
.put('/urls/' + url._id)
.send(url)
.expect(401)
.end(function(err, res) {
if (err) return done(err);
expect(res.body).to.equal('Count of the click is updated');
done();
});
});
});
I run to execute the unit tests and get the errors:
I read the articles by unit tests.
First article: http://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/
Second article: https://www.codementor.io/olatundegaruba/integration-testing-supertest-mocha-chai-6zbh6sefz
I don't understand why I get these errors. Please, help me. I think that I made little error, but since I cannot fint it.
UPDATED
I added route:
var express = require('express');
var GoogleUrl = require('google-url');
var _ = require('lodash');
var token = require('../middlewares/token');
var Url = require('../models/url');
var config = require('../config');
var router = express();
router.post('/create', token.required, createShortUrl);
router.put('/count/:id', token.required, updateCountClick);
router.put('/:id', token.required, updateUrlById);
module.exports = router;
function createShortUrl(req, res) {
_.trim(req.body.list_tags);
var tags = _.split(req.body.list_tags, ',');
tags.splice(tags.length - 1, 1);
var date = returnDate();
var time = returnTime();
var googleUrl = new GoogleUrl({
'key': config.get('google_key')
});
googleUrl.shorten(req.body.full_url, function (err, shortUrl) {
if (err) {
res.status(500).json(err);
}
var url = new Url({
'author': req.payload.username,
'description': req.body.description,
'full_url': req.body.full_url,
'short_url': shortUrl,
'list_tags': tags,
'date': date,
'time': time
});
url.save(function (err, url) {
if (err) {
return res.status(500).json(err);
} else {
return res.status(200).json(url);
}
});
});
}
function updateCountClick(req, res) {
var count_click = req.body.count_click + 1;
Url.findOneAndUpdate({_id: req.params.id}, {$set: {count_click: count_click}}, {new: true}, function (err, url) {
if (err) {
return res.status(500).json(err);
}
if (url) {
return res.status(200).json('Count of the click is updated');
}
});
}
function updateUrlById(req, res) {
_.trim(req.body.list_tags);
var tags = _.split(req.body.list_tags, ',');
tags.splice(tags.length - 1, 1);
Url.findOneAndUpdate({_id: req.params.id}, {$set: {description: req.body.description, list_tags: tags}}, {new: true}, function (err, url) {
if (err) {
res.status(500).json(err);
}
if (url) {
res.status(200).json(url);
}
});
}
UPDATED 2
Authoziration was added:
var token;
beforeEach(function (done) {
agent
.post('http://localhost:8000/auth/login')
.send({email: 'Keane95#yandex.ru', password: '123456'})
.end(function (err, res) {
if (err) {
return done(err);
}
expect(res.body.userData).to.have.property('token');
token = res.body.userData.token;
done();
});
});
Also I updated code my unit-tests.
I can't see where in your code you send 401 and Url. So it seems that your test requests are getting rejected by token.required middleware with 401 status code (which means "unauthorized").
.send(url)
.expect(401) // why do you expect 401? You never send it inside your logic
So basically your test never hit actual code.
First of all, you do need to fake authorization to make token.required middleware happy.
Then expect 200 result
.send(url)
.expect(200) // normal execution flow of createShortUrl results in 200
.end(/* rest of your test logic */)

Set authentication token to chai-Http

I have created some test cases in node express framework which return successfully. However there are some API calls which need to have authenticated before I can make them. I can call the API that requires authentication in reactJs via ajax and It give a success response, however when I set the same header in chai-Http, it fails with invalid token.
var chai = require('chai');
var should = chai.should();
var chaiHttp = require('chai-http');
chai.use(chaiHttp);
var server = require('../app');
describe('routes : index', function() {
var token = '';
beforeEach(function(done) {
done();
});
before(function(done) {
chai.request(server)
.post('/doSignin')
.send({uname:'ruzan#test.com',password:'123123'})
.end(function(err, res) {
res.redirects.length.should.equal(0);
res.status.should.equal(200);
token = res.body.user.token;
res.type.should.equal('application/json');
done();
});
});
afterEach(function(done) {
done();
});
describe('Test FolderController', function() {
it('should list all the folders', function(done) {
console.log(token); // here it print the same token
chai.request(server)
.get('/notes/get-notes')
.set('prg-header' , token)
.end(function(err, res) {
// console.log(res);
res.status.should.equal(200);
done();
});
});
});
});
ReactJs:
$.ajax({
url: '/notes/get-notes',
method: "GET",
dataType: "JSON",
headers: { 'prg-header':loggedUser.token }
}).done( function (data, text) {
console.log(data);
if(data.status.code == 200){
// it come here the data
}
}.bind(this));

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.

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.

Resources