如何testing使用JWTauthentication的Node API(用户login获取令牌)

TL; DR – testing使用JWT进行身份validation的Node API(Express)中的资源的方法是什么?凭证本身只授予用户名/密码login名?

我有点新的testing,并希望得到一些build议。 最终目标是要有一个经过全面testing的API,然后开始学习如何把这个连接到一个持续集成解决scheme。

正在使用的技术

  • 我使用ExpressNode中编写了一个API。
  • Mongo是数据库。
  • mongoose被用作ODM。
  • jsonwebtoken包用于创build/validation令牌。
  • Passport用于在路由上轻松地将用户身份validation添加为Express中间件。

API信息

API有各种资源 – 其中的具体细节对于这个查询并不重要,但为了简单起见,我们只是假装它是无处不在的Todo应用程序。

保存在数据库中的每个单独的资源都与一个用户相关联。

API使用JWT跨各种资源端点进行身份validation。 令牌本身包含唯一的用户ID,该ID是针对Mongo数据库中的资源存储的。 要获取令牌本身需要用户首先注册(返回令牌),然后login以获取新的令牌。

假装代码。

我将简化下面的代码,而不是使用任何环境configuration等等…

app.js

var express = require('express'); var app = express(); var mongoose = require('mongoose'); var bodyParser = require('body-parser'); var passport = require('passport'); mongoose.connect('mongodb://localhost/somedatabasename'); app.set('port', process.env.PORT || 3000); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(passport.initialize()); // ... Passport JWT Strategy goes here - omitted for simplicity ... var userRouter = require('./api/users/routes'); app.use('/users', userRouter); var todoRouter = require('./api/todos/routes'); app.use('/todos', todoRouter); app.listen(app.get('port'), function() { console.log('App now running on http://localhost:' + app.get('port')); }); 

./api/todos/routes.js

 var router = require('express').Router(); var controller = require('./controller'); var passport = require('passport'); router.route('/') .all(passport.authenticate('jwt', { session: false})) .get(controller.getAll) .post(controller.create); router.route('/:id') .all(passport.authenticate('jwt', { session: false})) .get(controller.getOne) .put(controller.update) .delete(controller.delete); module.exports = router; 

./api/users/routes.js

 var router = require('express').Router(); var controller = require('./controller'); var passport = require('passport'); router.route('/') // User signup .post(controller.create); router.route('/me') // User Login .post(passport.authenticate('local', { session: false}), controller.login) // Get current user's data .get(passport.authenticate('jwt', { session: false}), controller.getOne) // Update current user's data .put(passport.authenticate('jwt', { session: false}), controller.update) // Delete current user .delete(passport.authenticate('jwt', { session: false}), controller.delete); module.exports = router; 

./api/users/model.js

 var mongoose = require('mongoose'); var bcrypt = require('bcrypt'); var UserSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true } }); // ... for simplicity imagine methods here to // - hash passwords on a pre save hook using bcrypt // - compare passwords using bcrypt when logging in module.exports = mongoose.model('User', UserSchema); 

./api/todos/model.js

 var mongoose = require('mongoose'); var Schema = mongoose.Schema; var momentSchema = new Schema({ title: { type: String }, // Bunch of other fields here... _user: { type: Schema.Types.ObjectId, ref: 'User' } }); module.exports = mongoose.model('Moment', momentSchema); 

我已经省略了一些示例代码来保持简洁。

例如, 用户的控制器将包括模型及其function将:

  • controller.create – 注册新用户(返回令牌)
  • controller.login – 在通过Passport Local确认的用户名/密码组合之后,返回一个有效的标记
  • controller.getOne – 基于从JWT token获取的User ID,使用Mongoose从Mongo返回用户的数据。
  • controller.update – 使用Mongoose更新Mongo中的用户数据
  • controller.delete – 使用Mongoose删除Mongo中的用户数据

Todo的控制器会做类似的事情 – 只是通过Mongoose与Mongo数据交互,但是查询总是包含用户ID,以将特定的(例如)Todo项目与经过authentication的用户(通过JWT进行身份validation)相关联。

testing难题

如何使用Mocha,Chai和SuperTest的组合来testing这样的testing?

我可以吗:

  • 在Mongo中创build一个testing数据库,并在testing中使连接string不同? 这意味着要保存数据库中存储的实际数据以供testing。
  • 以某种方式模拟数据,而不是使用testing数据库? 但是,如何处理用户保存/login来检索令牌?

在开发过程中如何进行本地testing,或者使用某种CI工具进行部署时(我在学习中还没有达到的东西)?

任何援助将不胜感激,我希望我已经提供了足够的信息与上面的虚拟数据/代码:/

在testing过程中,你通常会嘲笑你的mongo数据库(类似于mongo-mock 。这样,你不需要运行实际的数据库来运行你的testing(你不是testing数据库,而是你的代码)。

在testing过程中,您将用mongo-mockreplacemongodb ,然后运行testing。 要得到您的令牌,您需要使用有效的模拟凭证发布到您的/meurl,该terminal将返回令牌,然后在您的下一个呼叫中使用该令牌来testing您的其他terminal。

在事物的令牌方面,我通常在进入其他端点之前在请求开始时检查它。 (我没有使用护照,但是这个想法是):

 app.use(validate_jwt_middleware); app.use('/users', userRouter); 

这样,如果令牌无效,则整个网站,而不是您的部分是无效的。

另外,我并没有使用SuperTest,而是使用chai-http,所以我无法帮助您解决具体问题。

希望这可以帮助,