Node Express Unit Test JSON Response - node.js

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

Related

Node.js - Testing REST API routes with supertest

Req.body is not accessible in the routes while making a post request. It would be highly appreciative of someone if he/she help me getting through it. Here is screenshot of my microservice.test.js file. Am I missing something?
import request from "supertest";
import mongoose from "mongoose";
import config from "../config/env";
import routes from "../server/routes";
import { parseResponse, doRequest } from "../server/utils/helperFunctions";
const app = express();
app.use("/", routes);
jest.setTimeout(30000);
The code provided doesn't provide much insight, as I would expect all of the handling of the request to be in your route handler. Is the issue that you are unable to access the body when running tests with supertest? Or that it isn't working at all. More information would be very helpful.
If it is a supertest issue, I would recommend checking out the docs for good examples. Here is one I pulled directly from the NPM site where they POST some data with a request body and then verify the response body:
describe('POST /user', function() {
it('user.name should be an case-insensitive match for "john"', function(done) {
request(app)
.post('/user')
.send('name=john') // x-www-form-urlencoded upload
.set('Accept', 'application/json')
.expect(function(res) {
res.body.id = 'some fixed id';
res.body.name = res.body.name.toLowerCase();
})
.expect(200, {
id: 'some fixed id',
name: 'john'
}, done);
});
});
Also, if you are trying to test your server you should probably import your server code instead of creating a new express instance. For example, in your server code you'll have something like this:
server.js
const express = require('express');
const app = express();
app.use('/', ...) // middleware/route config
...
module.exports = app;
Your server would then use this server like this:
index.js
const app = require('./server')
const port = 4000
app.listen({ port }, () => {
const location = `http://localhost:${port}`
logger.info(`🚀 Server ready at ${location}`)
})
module.exports = app
Now that you have structured your code this way, in your test you can import your server as well (so you are testing your actual server, not a new server that you made up):
server.test.js
const app = require('../../../server');
const request = require('supertest')(app);
describe('Server', () => {
it('does a thing', async () => {
const { body } = await request
.post('http://path/to/test')
.send({ data: 'some data' })
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200);
expect(body.thing).toBeTrue();
});
});
expressjs version lower than 4 include body parsing middleware
import bodyParser from 'body-parser';
app.use(bodyParser());
example test
it('.post should work with data', function (done) {
var app = express();
app.use(bodyParser());
app.post('/', function(req, res){
res.send(req.body.name);
});
request(app)
.post('/')
.send({ name: 'tobi' })
.expect('tobi', done);
})

Mocha, Chai & Sinon: Checking internal working of an API

Let's suppose I have a POST endpoint /user/:id and this endpoint, internally calls a function getUserData(id) and then returns the result to the caller, which in-turn returns the output, after JSON.stringify()ing.
Now, I need to ensure that getUserData(id) is called, for at-least once. How can I stub / spy getUserData(id) function, when I am using chai-http to make a post request to the server? Is it even a correct approach?
I adapted the tutorial from https://scotch.io/tutorials/test-a-node-restful-api-with-mocha-and-chai down to a barebones server and test that you can use to do a basic API test.
As Mr.Phoenix said, you don't need to get too deep into the nitty gritty of your handler, just pass some data to your endpoint and check the result against what you expect to get.
Here are 2 files you can use to do this test:
index.js
const express = require('express')
const app = express()
app.get('/material',(req, res)=>{
res.json([])
//res.json(['stone', 'wood'])
})
function getUserData(id){
return 42
}
const port = 3031
app.listen(port, function(err){
console.log("Listening on port: " + port)
})
module.exports = app
test.js
process.env.NODE_ENV = 'test'
// const Material = require('./materials') // conroller
const chai = require('chai')
const chaiHttp = require('chai-http')
const server = require('./index')
const should = chai.should()
chai.use(chaiHttp)
describe('/GET material', () => {
it('it should get all the materials', (done)=>{
chai.request(server)
.get('/material')
.end((err, res) => {
res.should.have.status(200)
res.body.should.be.a('array')
res.body.length.should.be.eql(0) // start empty
done()
})
})
})

How to unit test express route with passport authenticate

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.

how to use sinon js with Express js unit testing

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.

How do I integration test a node express controller?

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.

Resources