We started a node application where we are going to use Express as the router. In one of our controllers, we have this defined:
var express = require('express');
var router = express.Router();
// api/products
router.get('/', function (req, res) {
res.json({ products: [] });
});
// api/products/:id
router.get('/:id', function (req, res) {
res.json({ id: req.params.id });
});
module.exports = router;
I've been trying to follow examples on how to test this and came up with:
var hippie = require('hippie');
var server = require('../controllers/products');
describe('Products Controller Tests', function () {
describe('/products endpoint', function () {
it('returns an array of products', function (done) {
hippie(server)
.json()
.get('/products')
.expectStatus(200)
.end(function (err, res, body) {
if (err) throw err;
done();
});
});
});
});
But I'm always getting the following error:
Test Name: Products Controller Tests /products endpoint returns an array of products
Test Outcome: Failed
Result StandardOutput:
not ok 1 Products Controller Tests /products endpoint returns an array of products
TypeError: Cannot read property 'apply' of undefined
at Immediate. (C:\Api\node_modules\express\lib\router\index.js:618:14)
tests 1
pass 0
fail 1
What is the best way to unit test these controllers? Is there a different library I should be using?
What about extracting the behaviour of your routes and unit testing that?
Something like this
var express = require('express'),
router = express.Router(),
routes = require('./routes');
router.get('/:id', routes.getId);
So that in your unit test you can have
describe('getId', function() {
var route = require('../routes').getId;
var req = // build your request object with params and so on
var res = // stub this, for example with sinon
it('whatever', function() {
route(req, res);
expect(res.send.calledOnce).to.be.true;
// and so on
});
});
Once you're covered this with unit tests (faster to run, you can hook them up on pre-commit hook), you can integration test the route, without the need to mock the underlying HTTP library, with proper calls using request for example.
Related
I recently started learning node.js, I started to make an application on node.js + express + postgresql, as in the example presented here Rest api node postgre (pg-promise) did not understand how to output to html for subsequent parsing in a table, for example the result of the function " getAllPuppies ", and how to render it in pug. Example routes, app.js, queries.js (server.js), public and the table itself is an example of psql. I can not really figure out how to pass a value from the routes/index.js
var express = require('express');
var router = express.Router();
var db = require('../queries');
// router.get('/', db.getAllAlarms);
router.get('/Unix/:unixtime', db.getSingleCDRUnix);
//router.put('/api/alarms/:id', db.updateAlarm);
//router.delete('/api/alarms/:id', db.removeAlarm);
router.get('/index', function (req, res) {
res.render('index',{?????});
});
module.exports = router;
Pug
view
html
head
title= title
body
p
h1= 'Oooomphf'
a= {????}
hr
The postgres library uses promises, so you will need to resolve the asynchronous query before returning the data with res.render, similar to how the API responses work in the REST examples.
router.get('/index', function (req, res, next) {
db.getAllAlarms()
.then(all_alarms => {
res.render('index', { obj: all_alarms });
})
.catch(next)
});
The equivalent code using ES2017 async/await (Node 7.6+)
router.get('/index', async function (req, res, next) {
try {
let all_alarms = await db.getAllAlarms()
res.render('index', { obj: all_alarms });
catch (err) {
return next(err)
}
});
How can one unit test an express router that is dependent on passport authentication to call the helper methods?
I'm new to express unit testing and I've seen a lot of code that actually hits the server to call the method. But would that not make it an integration test? This ultimately comes down to my lack of understanding on the best practices on express unit testing.
I've tried to just mock out the passport but that didn't work because I need to get to the callbacks. I've also tried using rewire and just try to test the helper methods and that didn't seem to work either, I think, because file is wrapped in module.export.
Any help here would be much appreciated.
File I'm trying to unit test:
module.exports = function (inject) {
var router = require('express').Router();
var app = inject.app;
return router.get('/', app.passport.authenticate('bearer', { session: false }), [editContentCheck, getUser]);
function editContentCheck(req,res,next) {
if(req.authInfo.scope.indexOf('readOwnUser') == -1) {
res.statusCode = 403;
return res.end('Forbidden');
}
return next();
}
function getUser(req, res) {
var authHeader = req.headers.authorization.split(' ');
var token = authHeader[1];
var models = require('../models');
models.AccessToken.getAccessToken(token,function(err,tokenObj) {
models.User.getUser(tokenObj.userId, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
res.send(JSON.stringify(user));
});
});
}
};
Check this repository, it has all You want: https://github.com/num8er/alttab-nodejs-challenge
Also a look at example and implement it as You wish:
1)server.js :
var
http = require('http'),
app = require('./app'); // app.js file
http.createServer(app).listen(8080);
2)app.js :
var
express = require('express'),
app = express();
app.use(require('./routes')); // routes.js file
module.exports = app;
3)routes.js :
var router = require('express').Router();
function editContentCheck(req,res,next) {}
function getUser(req, res) {}
router.get('/posts', app.passport.authenticate('bearer', { session: false }), [editContentCheck, getUser]);
module.exports = router;
4)spec/AppSpec.js :
var
request = require('supertest-as-promised'), // npm i --save-dev supertest-as-promised
app = require('./../app');
var token = "some token here";
describe('App', function() {
describe("Posts", function() {
it('should pass auth check and get posts', function() {
return request(app)
.get('/posts')
.set('Authorization', 'Bearer ' + token)
.expect(200);
});
});
});
p.s. I'm using jasmine as testing framework, but even with mocha it's same style. Because of it's using supertest-as-promised that gets app module and calls the route without creating http object.
p.s.2. it's not unit testing, You're testing the feature, so it's more an integration test to check if all chains of code is properly integrated.
Hi i want to do unit testing on my express js code i want to mock data so after searching multiple websites and blogs i found this library but i am not clear how can i use this library for mocking or data.
My testing code is
var request = require('supertest');
var server = require('./app');
var chai = require('chai');
var chaiHttp = require('chai-http');
var server = require('./app');
var should = chai.should();
chai.use(chaiHttp);
describe('loading express', function () {
it('responds to /', function testSlash(done) {
request(server)
.get('/')
.expect(200, done);
});
it('404 everything else', function testPath(done) {
request(server)
.get('/foo/bar')
.expect(404, done);
});
it('responds to /customers/getCustomerData', function testPath(done) {
request(server)
.get('/customers/getCustomerData?id=0987654321')
.end(function(err, res){
res.should.have.status(200);
res.body.should.be.a('object');
res.body.status.should.equal("success");
res.body.data.customerId.should.equal("0987654321");
done();
});
});
});
Currently this code is fetching data from database but i want Unit testing using mock data. How can i achieve this?
__EDIT__
I want to test the code which is written inside Express js routes file. this routes i am calling inside app.js file like this
var customers = require('./routes/customers');
app.use('/customers', customers);
now the code which customers route file contain is
function getCustomerData(req, res, next) {
var response = {};
var cb = function (response) {
res.send(response);
}
var modelObj = req.models.customer_master;
var data = req.query;
controllers.customers.get(modelObj, data, cb);
};
router.get('/getCustomerData', function (req, res, next) {
getCustomerData(req, res, next);
});
I want to test the response of "get" method using mock data
You want to stub your controller middlewares i guess. As you didn't provide any server side code, i just asume some things:
app.get('/', rootController.get);
Now you want to stub this controller:
it('responds to /', function testSlash(done) {
const rootController = require('./path/to/your/controller');
const rootControllerStub = sinon.stub(rootController, "get",
function(req, res, next){
res.status(200).json({stubbed: 'data'});
});
request(server)
.get('/')
.expect(200)
.expect({stubbed: 'data'})
.end(done);
});
If you wish to mock, you can use sinon express mocks here or if you want to test the actual response data, JSON, use this example
The express route, in the example, takes a parameter and returns a JSON
it('should respond with JSON data', function (done) {
request(server)
.get('/about/jv')
.expect(200)
.end(function (err, response) {
assert.equal(response.header['content-type'], 'application/json; charset=utf-8');
assert.deepEqual(response.body, {
"data":{
"username":"hellojv"}
});
done();
});
but as mentioned above, if you want to use sinon then use the mock library. The example uses Mocha and supertest.
Additionaly, if you write many test files, the stub may not work because of cache. I have to clear cache before initializing the stub and the server. The order is also important.
// have to clear every module which belongs to the require chain
// APP require FOO ROUTE require FOO CONTROLLER require BAR LIB
const caches = [
'../app',
'../routes/foo',
'../controller/foo',
];
caches.forEach(cache => {
delete require.cache[require.resolve(cache)];
});
// mock
const bar = require('../lib/bar');
sinon.stub(bar, 'bar').callsFake(async function() {
return null;
});
app = require('../app');
// ... then the test ...
I found this thread helpful.
Just created a new open source middleware project for express. I want to be able to unit test the json response from the routes it generates... Is there anyway I can do this without actually firing up a grunt serve and checking the url?
So goal would be for someway to run the route but instead of sending json to the browser I can store it in a variable / etc...
What the middleware does is generates routes based on annotations in javascript files.
https://github.com/kmgilbert100/annotation-route-loader
I would like to make my unit test include testing the JSON responses you can see in the above url under tests/routes/**/*
Note app.use(loader) would load all the routes
Below is the current mocha test
// npm modules
const chai = require('chai');
const _ = require('lodash');
const express = require('express');
// local modules
var routeLoader = require('../src/index');
// testing module methods
const assert = chai.assert;
describe('annotation-route-loader', () => {
// store collection of routes
var routePaths = [];
before("Create collection to check from", () => {
var loader = routeLoader({
baseUrl: '/',
path: './routes',
pattern: '**/*.js',
params: {
sports: [
'footbal',
'baseball',
'motocross',
'hockey'
]
}
});
loader['stack'].forEach( stack => {
routePaths.push({
path: stack.route.path,
methods: stack.route.methods
})
})
});
it('Should make sure the default path is valid', (done) => {
// Try And Find Path
var defaultPath = _.find(routePaths, {path: '/'});
assert.isObject(defaultPath);
assert.isTrue(defaultPath.methods.get);
// Make Callback
done()
});
it('Should make sure the sports path is valid', (done) => {
// Try And Find Path
var defaultPath = _.find(routePaths, {path: '/sports'});
assert.isObject(defaultPath);
assert.isTrue(defaultPath.methods.get);
// Make Callback
done()
});
it('Should make sure the sports list path is valid', (done) => {
// Try And Find Path
var defaultPath = _.find(routePaths, {path: '/sports/list'});
assert.isObject(defaultPath);
assert.isTrue(defaultPath.methods.get);
// Make Callback
done()
});
})
Thanks for the comments!
https://github.com/visionmedia/supertest
Ended up using supertest to get the job done!
See below snippet...
var request = require('supertest');
var express = require('express');
var app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'tobi' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});
I am trying to get my test suite to run within my express.js app, but this is my first express.js app as well as my first time working with mocha and I am unsure how to set everything up.
Here is my test
should = require 'should'
assert = require 'assert'
expect = require 'expect'
request = require 'superagent'
mongoose = require 'mongoose'
config = require '../../config/config'
describe "POST", ->
app = require('../../app')
beforeEach (done)->
mongoose.connect config.db, (err) ->
if err
console.log "Error! #{err}"
done()
describe '/users/:id/activities', ->
it 'should return created document', (done) ->
request(app).post('http://localhost:3000/users/1/activities').send(
description: "hello world"
tags: "foo, bar"
).set('Content-Type','application/json')
.end (e, res) ->
console.log(res.body.description)
expect(res.body.description).should.equal('hello world')
done()
A couple of issues I am having are... 1) In the test suite it can't connect to my test db (in my config file); 2) I am getting this error message when trying to post
1) POST /users/:id/activities should return created document:
TypeError: Object #<Request> has no method 'post'
Can someone point me in the right direction on how to get everything running properly?
The error I am getting back from trying to connect to the MongoDB is Error! Error: Trying to open unclosed connection.
I am running the mocha suite by running this command
NODE_ENV=test mocha test/controllers/activities.test.coffee --compilers coffee:coffee-script/register -R spec
Edit
activities.coffee (routes)
express = require 'express'
router = express.Router()
mongoose = require 'mongoose'
Activity = mongoose.model 'Activity'
module.exports = (app) ->
app.use '/', router
router.post '/users/:id/activities', (req, res, next) ->
activity = Activity(
user_id: req.params.id
description: req.body.description,
tags: req.body.tags
)
activity.save (err)->
if not err
res.json(activity)
else
res.json({ error: err })
router.get '/users/:id/activities/:activity_id', (req, res, next) ->
Activity.findById req.params.activity_id, (err, activity) ->
if not err
res.json(activity)
else
res.json({ error: err })
app.js
require('coffee-script/register');
var express = require('express'),
config = require('./config/config'),
fs = require('fs'),
mongoose = require('mongoose');
mongoose.connect(config.db);
var db = mongoose.connection;
db.on('error', function () {
throw new Error('unable to connect to database at ' + config.db);
});
var modelsPath = __dirname + '/app/models';
fs.readdirSync(modelsPath).forEach(function (file) {
if (/\.coffee$/.test(file)) {
require(modelsPath + '/' + file);
}
});
var app = express();
require('./config/express')(app, config);
app.listen(config.port);
exports.app = app;
First, once you have your superagent pointing to you app variable that holds you routes, middlewares and so on, there is no need to specify the url in the methods provided by superagent. You just need to provide the route, just like so:
request(app)
.post('/users/123/activities')
.end(function (err, response) {
});
Second, use the before statement provided by mocha instead of beforeEach, the second one will try to connect to mongo on every unit test you have.
For the first error
request has no method 'post'
Make sure you have installed superagent and you can find it on your node_modules folder.
Hope that helps!