Node.js 如何测试使用JWT身份验证的节点API(通过用户登录获取令牌)

Node.js 如何测试使用JWT身份验证的节点API(通过用户登录获取令牌),node.js,api,testing,mocha.js,Node.js,Api,Testing,Mocha.js,TL;DR-在使用JWT进行身份验证的节点API(Express)中测试资源的方法是什么?令牌本身仅授予用户名/密码登录 我对测试有点陌生,希望得到一些建议。最终目标是拥有一个经过充分测试的API,然后开始学习如何将其连接到持续集成解决方案 正在使用的技术 我已经在节点中使用Express编写了一个API Mongo是数据库 猫鼬用作ODM jsonwebtoken包用于创建/验证令牌 Passport用于在路由上轻松添加用户身份验证作为快速中间件 API信息 API有各种各样的资源——这些

TL;DR-在使用JWT进行身份验证的节点API(Express)中测试资源的方法是什么?令牌本身仅授予用户名/密码登录

我对测试有点陌生,希望得到一些建议。最终目标是拥有一个经过充分测试的API,然后开始学习如何将其连接到持续集成解决方案

正在使用的技术

  • 我已经在节点中使用Express编写了一个API
  • Mongo是数据库
  • 猫鼬用作ODM
  • jsonwebtoken包用于创建/验证令牌
  • Passport用于在路由上轻松添加用户身份验证作为快速中间件
API信息

API有各种各样的资源——这些资源的细节对于这个查询并不重要,但是为了简单起见,让我们假设它是无处不在的Todo应用程序

数据库中保存的每个资源都与单个用户关联

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

假装代码。

我将简化下面的代码,不使用任何环境配置等

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;
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/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;
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);
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);
./api/todos/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);
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);
为了保持简洁,我省略了一些示例代码

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

  • 控制器。创建-注册新用户(返回令牌)
  • 控制器。登录-在Passport Local确认用户名/密码组合后,返回有效令牌
  • controller.getOne-根据从JWT令牌检索到的用户ID,使用Mongoose从Mongo返回用户数据
  • 控制器。更新-使用Mongoose更新Mongo中的用户数据
  • 控制器。删除-使用Mongoose删除Mongo中的用户数据
Todo的控制器也会做类似的事情——只是通过Mongoose与Mongo数据交互,但查询总是包含用户ID,以将特定(例如)Todo项与经过身份验证的用户(通过JWT进行身份验证)相关联

测试难题

我将如何使用摩卡、柴和SuperTest的组合来测试类似的东西

我可否:

  • 在Mongo中创建一个测试数据库,测试中的连接字符串是否不同?这意味着保存数据库中存储的实际数据以供测试
  • 以某种方式模拟数据,而根本不使用测试数据库?但是,如何处理用户保存/登录以检索令牌
在开发时,与使用某种CI工具进行部署时(我的研究中还没有涉及到这一点),测试在本地是如何工作的


如果您能提供任何帮助,我将不胜感激。我希望我已经提供了足够的关于上述虚拟数据/代码的信息:/

在测试期间,您通常会模拟mongo DB(类似于
mongo mock
。这样,您就不需要运行实际的数据库来运行测试(您不是在测试数据库,而是在测试代码)

在测试期间,您将使用
mongo mock
替换
mongodb
,然后运行测试。要获取令牌,您需要使用有效的模拟凭据发布到
/me
URL,该端点将返回令牌,然后在下次调用时使用该令牌测试其他端点

在令牌方面,我通常在请求开始时检查它,然后再进入其他端点(我没有使用passport,但想法是):

这样,如果令牌无效,那么它对整个站点无效,而不仅仅是您的部分

另外,我没有使用SuperTest,而是使用http,因此我无法帮助您了解具体情况


希望这能有所帮助,

嗨!你明白了吗?我也在尝试实现这种测试。