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()
})
})
})
Related
I am trying to write tests for a simple endpoint which is protected. I have an auth() middleware in place which verifies the Authorization header and upon successful verification adds a key token to the req object.
The problem is that jest is directly calling the getCustomerProfile controller and skips executing auth() middleware because of which the controller says req.token is undefined. This is how my Jest setup looks like:
const express = require('express');
const mapRoutes = require('express-routes-mapper');
const privateRoutes = ('../routes/private_routes')
const auth = require('auth_middleware');
const setupAction = async () => {
const testapp = express();
const mappedPrivateRoutes = mapRoutes(privateRoutes, 'api/controllers/');
testapp.use(express.urlencoded({ extended: false }));
testapp.use(express.json());
testapp.use('/private', mappedPrivateRoutes);
testapp.all('/private/*', (req, res, next) => auth(req, res, next));
return testapp;
};
module.exports = { setupAction };
Below is how I am trying to test the protected endpoint:
const request = require('supertest');
const {
setupAction
} = require('./setup_jest');
const Customer = require('models/customer_model');
let test_api;
beforeAll(async () => {
test_api = await setupAction();
});
....
...
...
test('Customer | get profile', async () => {
let token = 'TestToken123'
res = await request(api)
.get('/private/customer/get-profile')
.set('Accept', /json/)
.set('Authorization', `Bearer ${token}`)
.send()
.expect(200);
expect(res.body.customer_id).toBeTruthy();
});
In auth.js I have tried adding console.log() statements but it has no effect. Nothing gets printed and the request goes straight to getCustomerProfile() controller without validating the authorization headers.
What's more interesting is that when the express app is running and I hit the private endpoint with the Authorization header using Postman it works completely fine and the request goes through auth() middleware.
It is only while running through jest, the middleware is getting skipped!
I am stuck here for hours now. Can anyone please help me out with this? Thank you in Advance :)
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);
})
I am trying to set up stubbing out of middleware using Sinon in my Node App.
When I run a mocha test and point directly to the test file it stubs out correctly. When I point to all my testing folder and run the tests recursively it does not stub out the middleware and fails.
File Structure:
test
functional
checklist
test.spec.js
lib
middleware
auth.js (this is what is being stubbed out)
test.spec.js
const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
chai.use(chaiHttp);
const should = chai.should();
// Requirements
const auth = require('../../../lib/middleware/auth')
describe('/checklist/checklistItemLevel',function() {
let checkTokenStub;
beforeEach(function(){
checkTokenStub = sinon.stub(auth,'checkToken').callsFake((req,res,next)=>{
console.log('Stubbed')
next()
});
})
afterEach(function(){
auth.checkToken.restore();
})
context('/ POST',function() {
it('should return hello',function(done){
chai.request(require('../../../server'))
.post('/api/v1/checklist/checklistItemLevel')
.end((err,res)=>{
res.should.have.status(200);
res.text.should.be.eql('Hello');
done(err);
})
})
})
})
router.js
const router = require('express').Router();
const controller = require('./controller')
const auth = require('../../../lib/middleware/auth')
router.post('/',auth.checkToken,(req,res,next)=>{
res.send('Hello');
});
module.exports = router;
Mocha Calls
mocha "**/*.spec.js" // <- Doesnt stub out middleware
mocha "test/functional/checklist/*.spec.js" // <- Stubs out successfully
Found the answer. Mocha is working properly. But the app is being cached by another test case so its running without the stubs
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;
});