如何测试使用JWT身份验证的Node API(使用用户登录获取令牌)

nod*_*oob 14 testing api mocha.js node.js

TL; DR - 测试使用JWT进行身份验证的Node API(Express)中的资源的方法是什么,只有授予用户名/密码登录权限的令牌?

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

使用的技术

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

API信息

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

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

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

假装代码.

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

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'));
});
Run Code Online (Sandbox Code Playgroud)

./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;
Run Code Online (Sandbox Code Playgroud)

./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;
Run Code Online (Sandbox Code Playgroud)

./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);
Run Code Online (Sandbox Code Playgroud)

./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);
Run Code Online (Sandbox Code Playgroud)

我省略了一些示例代码,以保持其简洁.

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

  • controller.create - 为新用户注册(返回令牌)
  • controller.login - 在Passport Local确认的用户名/密码组合之后,返回一个有效的令牌
  • controller.getOne - 基于从JWT令牌检索的用户ID,使用Mongoose从Mongo返回用户的数据.
  • controller.update - 使用Mongoose更新Mongo中的用户数据
  • controller.delete - 使用Mongoose删除Mongo中的用户数据

待办事项的控制器就可以做同样的事情-只是通过猫鼬蒙哥的数据交互,但查询总是包括用户ID设置为特定的(例如)待办事项与认证用户(通过JWT认证)相关联.

测试难题

我如何使用Mocha,Chai和SuperTest的组合来测试这样的东西?

我可以吗:

  • 在Mongo中创建一个测试数据库,并在测试中使连接字符串不同?这意味着保存存储在数据库中的实际数据以进行测试.
  • 以某种方式模拟数据而根本不使用测试数据库?但是,如何处理用户保存/登录以检索令牌?

在开发时,与使用某种CI工具进行部署时,测试将如何在本地进行(我在学习中还没有这样做)?

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

Dan*_*ure 2

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

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

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

app.use(validate_jwt_middleware);
app.use('/users', userRouter);
Run Code Online (Sandbox Code Playgroud)

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

另外,我没有使用 SuperTest,而是 chai-http,所以我无法帮助你了解你的具体情况。

希望这可以帮助,