Similar questions have been asked before & I have looked at & followed them but no luck:
Sinon stub being skipped as node express middleware
node express es6 sinon stubbing middleware not working
How to mock middleware in Express to skip authentication for unit test?
The general answer I get from these is that the module (app.js in my case) should be required AFTER the auth middleware method has been stubbed. I have done this, but still calls the original middleware:
src/app.js
const authentication = require('./authentication')
...
app.use(['/api/users', '/api/groups'], authentication.ensureAuthenticed])
module.exports = app
src/authentication.js
const { isValidAuth } = require('./lib')
exports.ensureAuthenticated = (req, res, next) => {
...
}
__helpers__/supertest.js
// This file just calls endpoints with supertest but this is the only file
// that includes 'app'
const app = require('../../src/app')
module.exports = {
post: {
...
},
get: {
...
},
put: {
...
},
delete: {
...
}
}
users.spec.js
const authentication = require('../../src/authentication')
const authenticationStubs = require('../__stubs__/authentication')
let supertest
let ensureAuthStub
describe('Users', () => {
before(() => {
ensureAuthStub = sinon.stub(authentication, 'ensureAuthenticated').callsFake(authenticationStubs.ensureAuthenticated)
supertest = require('../__helpers__/supertest')
})
// tests
after(() => {
ensureAuthStub.restore()
})
})
__stubs__/authentication.js
exports.ensureAuthenticated = (req, res, next) => {
...
}
In users.spec.js, I load in supertest.js (which loads in src/app.js) AFTER the method has been mocked so I am not sure why the original is still being invoked.
I have also attempted to manually clear the cache before mocking but still does not work.
I think the solution would be using Rewire instead (or with) Supertest.
Rewire lets you to mock top level components of a module. Though you would need to mock middleware before passing to Supertest.
Turns out it was something to do with having supertest.js require app.js. I now have users.spec.js require the app and pass it it into supertest methods as a param. It now works. Still unsure why though
Related
I have 2 models like this
const Db = mongoose.model('db', dbSchema);
const Beacon = mongoose.model('beacon', dbSchema2);
Now I want to export them. First I export Db and everything is fine. I can do an HTTP request with it.
module.exports = Db;
However, when I try to export ´the 2nd one outside, it stops functioning. The functions below will return a blank JSON file as a response.
module.exports = Db;
module.exports = Beacon;
This won't work either. It returns an error handler saying all my functions in the handler is not function.
module.exports = {
Db, Beacon
}
This is the function on the file I import the models.
router.get('/data/:id', function(req, res, next) {
Db.findOne({ _id: req.params.id }).then(function(db) {
res.send(db);
});
}
The return from the handler is Db.findOne is not a function.
Is there any way to export them both? Thank you.
Here is the importing on another file
const Db = require('./db.js');
const Beacon = require('.db.js');
This should work:
Exporting in one file
module.exports = { Db, Beacon };
Then, importing in another file
const { Db, Beacon } = require('path-to-db.js');
// use them
Db.doSomething();
Beacon.doSomethingElse();
Notice that this uses the ECMAS 6 Destructuring Assignment (additional info on MDN)
I have an express app with API endpoints secured using JWT token. I have a method that verifies a received token.
// authentication.js
import jwt from 'jsonwebtoken';
import Settings from '../settings';
const AuthenticationMiddleware = {
verifyToken: (req, res, next) => {
const token = req.headers['x-access-token'];
if (!token) {
const msg = 'Include a valid token in the x-access-token header';
return res.status(422).json({
error: 'No token provided',
msg
});
}
try {
req.user = jwt.verify(token, Settings.jwtSecret);
req.token = token;
return next();
}
catch (e) {
return res.status(422).json({ error: 'Invalid token' });
}
}
};
export default AuthenticationMiddleware;
This works fine when I call the API endpoints from postman with the token header included.
Now I have a test such as shown below. There's about 40 of them, each requiring a token to be sent with each API request.
// should is not used directly in the file but is added as a mocha requirement
import supertest from 'supertest';
import app from '../app';
const server = supertest.agent(app);
const BASE_URL = '/api/v1';
describe('/loans: Get all loans', () => {
it('should return a list of all loans', done => {
server
.get(`${BASE_URL}/loans`)
.expect(200)
.end((err, res) => {
res.status.should.equal(200);
res.body.data.should.be.an.instanceOf(Array);
for (const each of res.body.data) {
each.should.have.property('id');
each.should.have.property('userid');
}
done();
});
});
});
I've looked at sinon and tried stubbing the verifyToken function in mocha's before hook like so
import sinon from 'sinon';
import AuthenticationMiddleware from '../middleware/authentication';
before(() => {
const stub = sinon.stub(AuthenticationMiddleware, 'verifyToken');
stub.returnsThis()
});
But I can already see a problem here. While the verifyToken stub may have been created, it is NOT used during the test. The verifyToken that is being called during the test is passed as middleware from the route like so
router.get('/loans', AuthenticationMiddleware.verifyToken, LoansController.get_all_loans);
I want a way to stub verifyToken during the test so that I can just return next() immediately.
My question is, is it possible to stub AuthenticationMiddleware.verifyToken universally during the test so that all calls to the API endpoint call the stubbed version?
According to these two posts, Sinon stub being skipped as node express middleware and How to mock middleware in Express to skip authentication for unit test?, the reason for my stub not being active was that the app was being imported and cached before the stub is even created, so the app uses the one it has cached.
So the solution was to alter the required function before the app gets a chance to cache it. What I did was that (I stumble upon it by trial and error) was to create a file in my test folder called stubs.js and here's the content.
import sinon from 'sinon';
import AuthenticationMiddleware from '../middleware/authentication';
sinon.stub(AuthenticationMiddleware, 'verifyToken').callsFake(
(req, res, next) => next()
);
Then I require this file in my test runner in package.json like so
"scripts": {
"test": "nyc --reporter=html --reporter=text --reporter=lcov mocha -r #babel/register -r should -r test/stubs.js"
},
I am trying to learn how to create a facebook Bot.
I found this amazing article on Medium which illustrates how we can create a messenger bot
In this article, The author tells us to create a verification.js. file inside controllers/verification.js. and paste the following code in it.
module.exports = (req, res) => {
const hubChallenge = req.query[‘hub.challenge’];
const hubMode = req.query[‘hub.mode’];
const verifyTokenMatches = (req.query[‘hub.verify_token’] === ‘crowdbotics’);
if (hubMode && verifyTokenMatches) {
res.status(200).send(hubChallenge);
} else {
res.status(403).end();
}
};
This, I understand to be ES6 Anonymous function which executes immediately? And since we aren't doing anything like
var express = require("express");
var app = express();
I am assuming it to be simple Vanilla JavaScript file. Now in our app.js we just import like this const verificationController = require("./controllers/verficiation.js")
Now, The question which is bothering me is that how did we passed the arguments to this module.exports = (req, res) => {
Without reading the article or knowing anything about that bot, I assume you are going to use verificationController as middleware to an express route, like this:
app.get("/", verificationController, (req, res) => {...});
This will pass req and res as parameters to the function
I have an Express application with a router, here is the example of the router:
const router = require('express-promise-router')();
const users = require('./users');
const validate = require('./validate');
router.get('/users', users.list);
router.get('/users/:id', users.get);
// other routes here
module.exports = router;
Now I want to add a middleware that validates each query, like that (this is not the working example, it's just to show the idea of what I want to accomplish):
const schemas = {
'/users': 'some validation schema',
'/users/:id': 'another validation'
}
module.exports = (req, res, next) => {
const url = req.originalUrl; // This is where I'm stuck.
if (!schemas[url]) {
// throw new error that validation failed
}
// validating somehow
if (!req.validate(schemas[url])) {
// throw new error that validation failed
}
return next();
}
And for this, I need to get the middlelware mount folder (like '/users/:id' for '/users/557'). I've tried to use req.originalUrl, but it returns the full URL path instead of the mount folder.
How can I achieve this? And if there's no way, how can I write my validation middleware another way to make it work?
Inside req.route you will get the path of API.
Check this screenshot
I want to pass through some environment variables from a Koa server to the client. In express I could do something like res.render('index', { data: 'someData' }); and then I could access data. In Koa I can't see how to do this. It mentions using context.state but I can't find any example of how to retrieve this in the client.
You can do something similar in Koa, you just need to use the right middleware. Try out koa-views if you're using one of the supported engines.
Here is a full example (this example assumes you're using Koa v1 and EJS as your templating engine):
app.js
const Koa = require('koa')
const views = require('koa-views')
const router = require('./routes')
const app = new Koa()
app.use(views(__dirname + '/views', { extension: 'ejs' }))
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000)
routes.js
const router = require('koa-router')()
router.get('/', function * () {
yield this.render('index', { title: 'Home' })
})
router.get('/about', function * () {
yield this.render('about', { title: 'About' })
})
module.exports = router
Just change the extension argument you pass to the middleware based on which templating engine you are using.