mocha诞生于2011年,是一个特征丰富的javascript测试框架,能够运行在node.js和浏览器上,使异步测试更简单和有趣。mocha测试连续运行,容许灵活和准确的报告,同时将未捕获的异常映射到正确的测试用例。javascript
背景
公司项目中,没有自动化的单元测试,而是经过写if/else判断,多少有点懵逼html
所在在种种考虑之下,咱们就选择mocha测试框架作单元测试前端
测试报告
在terminal里运行java
npm run mochawesome
完成项目中的单元测试node
单击 file:///
+项目所在地址+/mochawesome-report/mochawesome.html
react
最终获得的就是这一份测试报告git
待测试的接口
须要测试的代码以下github
'use strict';
const router = require('express').Router();const passport = require('passport');const User = require('../collections/user');const log = require('../services/logger').createLogger('userAuthentication');const AUTH_ERR = require('../constant/errMessage').AUTH;const COMM_ERR = require('../constant/errMessage').COMMON;
/** * @api {get} /v1/auth/ User auth information * @apiName UserAuthInfo * @apiGroup userAuthentication * * @apiParam {null} null. * * @apiSuccess {String} username The username of the current user. * @apiSuccess {date} last User last logon time. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "test", * "last": "2019-06-03T06:22:53.567Z" * } * * @apiError NOT_LOGIN The current User was not logon. * * @apiErrorExample Error-Response: * HTTP/1.1 401 Unauthorized * { * "err": "NOT_LOGIN", * "message": "User has not logon in!" * } */
router.get('/', function(req, res) { if (req.user) { res.json({ username: req.user.username, last: req.user.last }); } else { res.status(401).json({ err: 'NOT_LOGIN', message: AUTH_ERR.NOT_LOGIN }); }});
/** * @api {post} /v1/auth/register User Register * @apiName UserRegister * @apiGroup userAuthentication * * @apiParam {String} username New user's name. * @apiParam {String} password New user's password. * * @apiSuccess {String} username The username of the register user. * @apiSuccess {string} message The registering success info. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "gushen", * "message": "User registered successful" * } * * @apiError REGISTER_FAILURE The register failure. * * @apiErrorExample Error-Response: * HTTP/1.1 500 Internal Server Error * { * "err": "REGISTER_FAILURE", * "message": "User register failure!" * } */router.post('/register', function(req, res, next) { User.register(new User({ username: req.body.username }), req.body.password, function(err) { if (err) { log.error(err); res.status(500).json({ err: 'REGISTER_FAILURE', message: AUTH_ERR.REGISTER_FAILURE }); return; }
log.info('user ' + req.body.username + ' registered successful!'); res.json({ username: req.body.username, message: 'User registered successful' });
});});
/** * @api {post} /v1/auth/login User login * @apiName UserLogin * @apiGroup userAuthentication * * @apiParam {String} username User's name. * @apiParam {String} password User's password. * * @apiSuccess {String} username The username of the register user. * @apiSuccess {string} message The messgaer if the user login in successful. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "test", * "message": "Authentication Success" * } * * @apiError REGISTER_FAILURE The register failure. * * @apiErrorExample Error-Response: * HTTP/1.1 401 Unauthorized * { * "err": "AUTHENTICATE_FAILURE", * "message": "Authenticate failure" * } */router.post('/login', isAhenticated, passport.authenticate('local'), function(req, res) { if (req.user) { log.info(`${req.user.username} login in successful`); res.json({ username: req.user.username, message: 'Authentication Success' }); return; } log.info(`${req.user.username} login failure`); res.status(401).json({ err: 'AUTHENTICATE_FAILURE', message: `${req.user.username} login failure` });
});
/** * @api {post} /v1/auth/user/:username User delete * @apiName UserDelete * @apiGroup userAuthentication * * @apiParam {String} username User's name. * * @apiSuccess {String} username The username of the deleted user. * @apiSuccess {string} message The message if deleting successful. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "gushen", * "message": "Delete User Successful" * } * * @apiError NOT_LOGIN The register failure. * * @apiErrorExample Error-Response: * HTTP/1.1 401 Unauthorized * { * "err": "NOT_LOGIN", * "message": "User has not logon in!" * } */router.delete('/user/:username', function(req, res) { if (!req.user) { res.status(401).json({ err: 'NOT_LOGIN', message: AUTH_ERR.NOT_LOGIN }); return; }
// if (!req.params.username) { // res.json({ // err: 'PARAMS_NOT_CORRECT', // message: 'No deleted user name' // }); // return; // }
User.deleteOne({ username: req.params.username }, (err) => { if (err) { log.error(err); res.status(500).json({ err: 'SERVER_ERROR', message: COMM_ERR.SERVER_ERROR }); return; }
res.json({ username: req.params.username, message: 'Delete User Successful' });
log.info(`${req.params.username} has been deleted`); });});
/** * @api {post} /v1/auth/changepassword User change password * @apiName UserChangePassword * @apiGroup userAuthentication * * @apiParam {String} username User's name. * @apiParam {String} oldpassword User's old password. * @apiParam {String} newpassword User's old password. * * @apiSuccess {String} username The username of the user. * @apiSuccess {string} message The message if changing password successful. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "test", * "message": "change password successful" * } * * @apiError AUTHENTICATE_FAILURE The register failure. * * @apiErrorExample Error-Response: * HTTP/1.1 401 Unauthorized * { * "err": "AUTHENTICATE_FAILURE", * "message": "Password or username is incorrect" * } */router.post('/changepassword', function(req, res) { User.findOne({ 'username': req.body.username }, (err, user) => { if (err) { log.error(err); res.status(500).json({ err: 'SERVER_ERROR', message: COMM_ERR.SERVER_ERROR }); return; }
if (!user) { res.status(500).json({ err: 'USER_NOT_EXIST', message: AUTH_ERR.USER_NOT_EXIST }); return; }
user.changePassword(req.body.oldpassword, req.body.newpassword, (err, value) => { if (err) { log.error(err); res.status(401).json({ err: 'AUTHENTICATE_FAILURE', message: err.message }); return; }
log.info(`${req.body.username} change password successful`); res.json({ username: req.body.username, message: 'change password successful' });
}); });});
/** * @api {get} /v1/auth/logout User login out * @apiName UserLogout * @apiGroup userAuthentication * * @apiSuccess {String} username The username of the user. * @apiSuccess {string} message The message if user login out successful. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "test", * "message": "logout successful" * } * * @apiError NOT_LOGIN There is no user logon in. * * @apiErrorExample Error-Response: * HTTP/1.1 401 Unauthorized * { * "err": "NOT_LOGIN", * "message": "No user has been logon" * } */router.get('/logout', function(req, res) { const user = req.user; if (!user) { res.status(401).json({ err: 'NOT_LOGIN', message: 'No user has been logon' }); return; }
// user login out req.logout(); if (!req.user) { res.json({ username: user.username, message: 'logout successful' });
log.info(`${user.username} has been logon out`); return; }
res.status(500).json({ err: 'SERVER_ERROR', message: 'logout failure!' });
});
function isAhenticated(req, res, next) { User.findOne({ 'username': req.body.username }, (err, user) => { if (err) { log.error(err); res.json({ err: 'SERVER_ERROR', message: COMM_ERR.SERVER_ERROR }); return; } // If user is not existed if (!user) { res.json({ err: 'USER_NOT_EXIST', message: AUTH_ERR.USER_NOT_EXIST });
return; }
user.authenticate(req.body.password, (err, value) => { if (err) { log.error(err); res.json({ err: 'SERVER_ERROR', message: COMM_ERR.SERVER_ERROR }); } else if (value) { return next(); } else { res.json({ err: 'AUTHENTICATE_FAILURE', message: AUTH_ERR.AUTHENTICATE_FAILURE }); } }); });}
module.exports = router;
这是一套常见的有关用户登陆注册验证的接口web
由于文本只涉及到这个模块,因此将这个模块的接口都写在userAuthentication
测试套件下express
describe('userAuthentication', function() {}
测试代码
'use strict';
const request = require('supertest');const url = 'http://localhost:5001';// eslint-disable-next-line no-unused-varsconst should = require('should');
var userCookie;
// 用户名密码const user = { username: 'name', password: 'password'};
// 测试更改密码(每次测试完调换)const user2 = { username: 'uu2', password: 'oldpassword'};
const newUser2 = { username: 'uu2', oldpassword: 'oldpassword', newpassword: 'newpassword'};
// const user22={// username: 'uu2',// password: 'newpassword'// };// const oldUser2 = {// username: 'uu2',// oldpassword: 'newpassword',// newpassword: 'oldpassword'// };
describe('userAuthentication', function() { // 测试注册接口 describe('UserRegister', function() { describe('POST /register', function() { // eslint-disable-next-line max-len it('register success', function(done) { request(url) .post('/api/v1/auth/register') .send(user) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'User registered successful' }); if (err) throw err; done(); }); }); it('repeated registration failure.', function(done) { request(url) .post('/api/v1/auth/register') .send(user) .expect(500) .end(function(err, res) { res.body.should.containEql({ err: 'REGISTER_FAILURE' }); if (err) throw err; done(); }); }); }); }); // 测试登陆接口 describe('UserLogin', function() { describe('POST /login', function() { it('login success', function(done) { request(url) .post('/api/v1/auth/login') .send(user) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'Authentication Success' }); if (err) throw err; done(); }); }); it('USER_NOT_EXIST.', function(done) { request(url) .post('/api/v1/auth/login') .send({ username: 'a', password: 'admin' }) .expect(200) .end(function(err, res) { res.body.should.containEql({ err: 'USER_NOT_EXIST' }); if (err) throw err; done(); }); }); }); }); // 权限验证 describe('UserAuthInfo', function() { describe('GET /api/v1/auth/', function() { // 没有登陆,权限验证 it('The current User was not login.', function(done) { request(url) .get('/api/v1/auth/') .set('Accept', 'application/json') .expect('Content-Type', /json/) .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'NOT_LOGIN' }); if (err) throw err; done(); }); }); // 权限验证前先登陆 beforeEach(function(done) { request(url) .post('/api/v1/auth/login') .send(user) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('The username of the current user.', function(done) { request(url) .get('/api/v1/auth/') .set('Cookie', userCookie) .expect(200) .end(function(err, res) { res.body.should.have.keys('username'); if (err) throw err; done(); }); }); }); }); // 测试用户注销接口 describe('UserLogout', function() { describe('GET /logout', function() { // 没有登陆,测试注销 it('NOT_LOGIN.', function(done) { request(url) .get('/api/v1/auth/logout') .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'NOT_LOGIN' }); if (err) throw err; done(); }); }); // 注销成功前先登陆 beforeEach(function(done) { request(url) .post('/api/v1/auth/login') .send(user) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('logout successful.', function(done) { request(url) .get('/api/v1/auth/logout') .set('Cookie', userCookie) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'logout successful' }); if (err) throw err; done(); }); }); }); }); // 测试更改用户密码接口 describe('UserChangePassword', function() { describe('POST /changepassword', function() { // 更改用户密码前先注册-登陆 // eslint-disable-next-line no-undef before(function(done) { request(url) .post('/api/v1/auth/register') .send(user2) .end(function(err, res) { if (err) throw err; done(); }); }); // eslint-disable-next-line no-undef before(function(done) { request(url) .post('/api/v1/auth/login') .send(user2) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('change password successful', function(done) { request(url) .post('/api/v1/auth/changepassword') .set('Cookie', userCookie) .send(newUser2) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'change password successful' }); if (err) throw err; done(); }); }); it('AUTHENTICATE_FAILURE', function(done) { request(url) .post('/api/v1/auth/changepassword') .set('Cookie', userCookie) .send(newUser2) .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'AUTHENTICATE_FAILURE' }); if (err) throw err; done(); }); }); // after(function(done) { // request(url) // .post('/api/v1/auth/login') // .send(user22) // .set('Accept', 'application/json') // .end(function(err, res) { // if (!err) { // userCookie = res.header['set-cookie']; // done(); // } // }); // }); // after(function(done) { // request(url) // .post('/api/v1/auth/changepassword') // .set('Cookie', userCookie) // .send(oldUser2) // .expect(200) // .end(function(err, res) { // res.body.should.containEql({ // message: 'rechange password successful' // }); // if (err) throw err; // done(); // }); // }); }); }); // 测试删除用户接口 describe('UserDelete', function() { describe('DELETE /user/:username', function() { it('NOT_LOGIN.', function(done) { request(url) .delete(`/api/v1/auth/user/${user.username}`) .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'NOT_LOGIN' }); if (err) throw err; done(); }); }); // 删除用户前先登陆 beforeEach(function(done) { request(url) .post('/api/v1/auth/login') .send(user) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('delete user success', function(done) { request(url) .delete(`/api/v1/auth/user/${user.username}`) .set('Cookie', userCookie) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'Delete User Successful' }); if (err) throw err; done(); }); }); }); });});
测试前的准备
测试框架
所谓"测试框架",就是运行测试的工具。经过它,能够为JavaScript应用添加测试,从而保证代码的质量。
一般应用会有单元测试(Unit tests)和功能测试(Functional tests),复杂大型应用可能会有整合测试(Integration tests)。
1.单元测试:关注应用中每一个零部件的正常运转,防止后续修改影响以前的组件。2.功能测试:确保其总体表现符合预期,关注可否让用户正常使用。3.整合测试:确保单独运行正常的零部件整合到一块儿以后依然能正常运行。
开发人员主要是集中单元测试,做为开发中的反馈。
单元测试的好处:
1.若是能经过单元测试,那么经过后续测试且软件总体正常运行的几率大大提升。2.单元测试发现的问题定位到细节,容易修改,节省时间。3.追踪问题变得更加方便。
选择单元测试框架
单元测试应该:简单,快速执行,清晰的错误报告。测试框架基本上都作了同一件事儿:
1.描述你要测试的东西2.对其进行测试3.判断是否符合预期
选择框架会考虑下面的点:
1.断言(Assertions):用于判断结果是否符合预期。有些框架须要单独的断言库。2.适合 TDD / BDD:是否适合 测试驱动型 / 行为驱动型 的测试风格。3.异步测试:有些框架对异步测试支持良好。4.使用的语言:大部分 js 测试框架使用 js。5.用于特定目的:每一个框架可能会擅长处理不一样的问题。6.社区是否活跃。注:7.TDD:测试驱动型的开发方式,先写测试代码,以后编写能经过测试的业务代码,能够不断的在能经过测试的状况下重构。8.BDD:与 TDD 很类似,测试代码的风格是预期结果,更关注功能,看起来像需求文档。
其实都是先写测试代码,感受BDD 风格更人性。
测试工具的类型
组合使用工具很常见,即便已选框架也能实现相似的功能
1.提供测试框架(Mocha, Jasmine, Jest, Cucumber)2.提供断言(Chai, Jasmine, Jest, Unexpected)3.生成,展现测试结果(Mocha, Jasmine, Jest, Karma)4.快照测试(Jest, Ava)5.提供仿真(Sinon, Jasmine, enzyme, Jest, testdouble)6.生成测试覆盖率报告(Istanbul, Jest, Blanket)7.提供类浏览器环境(Protractor, Nightwatch, Phantom, Casper)
解释上面提到的点:
1.测试框架,即组织你的测试,当前流行 BDD 的测试结构。2.快照测试(snapshot testing),测试 UI 或数据结构是否和以前彻底一致,一般 UI 测试不在单元测试中3.仿真(mocks, spies, and stubs):获取方法的调用信息,模拟方法,模块,甚至服务器
Jest/Jasmine/Mocha框架特色
•Jest
•facebook 坐庄•基于 Jasmine 至今已经作了大量修改添加了不少特性•开箱即用配置少,API简单•支持断言和仿真•支持快照测试•在隔离环境下测试•互动模式选择要测试的模块•优雅的测试覆盖率报告,基于Istanbul•智能并行测试(参考)•较新,社区不十分红熟•全局环境,好比 describe 不须要引入直接用•较多用于 React 项目(但普遍支持各类项目)
•Jasmine •开箱即用(支持断言和仿真)•全局环境•比较'老',坑基本都有人踩过了•AVA•异步,性能好•简约,清晰•快照测试和断言须要三方支持•Tape•体积最小,只提供最关键的东西•对比其余框架,只提供最底层的 API
•Mocha
•灵活(不包括断言和仿真,本身选对应工具)•流行的选择:chai,sinon•社区成熟用的人多,测试各类东西社区都有示例•须要较多配置•可使用快照测试,但依然须要额外配置
综上所述,Mocha 用的人最多,社区最成熟,灵活,可配置性强易拓展,Jest 开箱即用,里边啥都有提供全面的方案,Tape 最精简,提供最基础的东西最底层的API。因此本文就选择用mocha。
mocha特征
1.浏览器支持2.全局变量泄漏检测3.简单异步支持,包括promise4.能够选择运行与regexp匹配的测试5.测试覆盖率报告6.自动退出以防止活动循环“挂起”7.字符串差别支持8.易于元生成套件和测试用例9.用于运行测试的javascript API10.配置文件支持11.CI支持等的正确退出状态12.mocha.opts文件支持13.自动检测和禁用非tty的着色14.可单击套件标题以筛选测试执行15.将未捕获的异常映射到正确的测试用例16.节点调试器支持17.异步测试超时支持18.检测多个要完成的调用19.测试重试支持20.测试特定超时21.咆哮支持22.报告测试持续时间23.突出显示慢速测试24.文件监视程序支持25.使用所需的任何断言库26.extensible reporting, bundled with 9+ reporters27.可扩展测试DSL或“接口”28.每一个钩子以前、以后、全部钩子以前、以后29.任意蒸腾器支持(coffee-script 等)30.TextMate bundle
断言库should
Mocha自己是不包含断言库的,因此咱们须要本身选择断言库。should是一个很简单的、贴近天然语言的断言库。固然,Mocha是适配全部的断言库的,若是你喜欢其余的断言库好比expect之类的,你也能够把它包含进来使用。
http测试模块SuperTest
单单使用Mocha和should就几乎能够知足全部JavaScript函数的单元测试。可是对于Node应用而言,不只仅是函数的集合,好比一个web应用的测试。这时候就须要配合一个http代理,完成Http请求和路由的测试。Supertest是一个HTTP代理服务引擎,能够模拟一切HTTP请求行为。Supertest能够搭配任意的应用框架,从而进行应用的单元测试。
测试套件describe
describe块称为"测试套件"(test suite),表示一组相关的测试。它是一个函数,第一个参数是测试套件的名称("加法函数的测试"),第二个参数是一个实际执行的函数。
测试用例it
it块称为"测试用例"(test case),表示一个单独的测试,是测试的最小单位。它也是一个函数,第一个参数是测试用例的名称("1 加 1 应该等于 2"),第二个参数是一个实际执行的函数。
钩子hooks
Mocha在describe块之中,提供测试用例的四个钩子:before()、after()、beforeEach()和afterEach()。它们会在指定时间执行。
describe('hooks', function() {
before(function() { // 在本区块的全部测试用例以前执行 });
after(function() { // 在本区块的全部测试用例以后执行 });
beforeEach(function() { // 在本区块的每一个测试用例以前执行 });
afterEach(function() { // 在本区块的每一个测试用例以后执行 });
// test cases});
安装mocha> = v3.0.0,npm的版本应该> = v2.14.2。除此,确保使用Node.js的版本> = v4来运行mocha
mocha小例子
安装
做为项目的依赖进行安装
npm install --save-dev mocha
开始
mkdir testcd testtouch test.js
加入测试代码
'use strict'var assert = require('assert');describe('Array', function() { describe('#indexOf()', function() { it('should return 0 when the value is not present', function() { assert.equal([1, 2, 3].indexOf(1), 1); }); });});
安装依赖
npm install --save-dev assert
执行测试
./node_modules/mocha/bin/mocha
报错结果
Array #indexOf() 1) should return 0 when the value is not present
0 passing (5ms) 1 failing
1) Array #indexOf() should return 0 when the value is not present:
AssertionError [ERR_ASSERTION]: 0 == 1 + expected - actual
-0 +1
at Context.<anonymous> (test/test.js:6:14)
package.json中写入命令
"mocha": "mocha"
执行命令
npm run mocha
正确测试
'use strict'var assert = require('assert');describe('Array', function() { describe('#indexOf()', function() { it('should return 0 when the value is not present', function() { assert.equal([1, 2, 3].indexOf(1), 0); }); });});
正确结果
Array #indexOf() ✓ should return 0 when the value is not present
1 passing (4ms)
到这里,对mocha就有了初步的认识
开始测试
了解了背景和框架后,正式开启测试
添加依赖
npm install --save-dev mocha mochawesome should supertest
在scripts中添加命令
"mochawesome": "./node_modules/.bin/mocha --reporter mochawesome","dev": "node index.js"
mochawesome生成报告
dev启动项目
注册接口的测试
1.从注册接口中,我得知该接口返回两个状态码,分别是200和500,对应的注册成功和注册失败2.那么测试中就有两个注册成功和失败的测试用例3.每一个测试用例针对每一个状态返回的值判断4.经过便可5.不经过,要么是接口有问题,要么是写的测试有问题
/** * @api {post} /v1/auth/register User Register * @apiName UserRegister * @apiGroup userAuthentication * * @apiParam {String} username New user's name. * @apiParam {String} password New user's password. * * @apiSuccess {String} username The username of the register user. * @apiSuccess {string} message The registering success info. * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK * { * "username": "gushen", * "message": "User registered successful" * } * * @apiError REGISTER_FAILURE The register failure. * * @apiErrorExample Error-Response: * HTTP/1.1 500 Internal Server Error * { * "err": "REGISTER_FAILURE", * "message": "User register failure!" * } */router.post('/register', function(req, res, next) { User.register(new User({ username: req.body.username }), req.body.password, function(err) { if (err) { log.error(err); res.status(500).json({ err: 'REGISTER_FAILURE', message: AUTH_ERR.REGISTER_FAILURE }); return; }
log.info('user ' + req.body.username + ' registered successful!'); res.json({ username: req.body.username, message: 'User registered successful' });
});});
1.UserRegister
是第二层套件2.POST /register
是第三层套件3.register success
是测试用例名称4.it的方法是测试用例的方法5.请求url地址6.发送post请求7.发送用户名和密码8.断言状态码9.断言返回的值10.执行下一步
注意: 每一个测试用例结束后必须带上done
,不然没有结束标识,会超时报错
// 测试注册接口 describe('UserRegister', function() { describe('POST /register', function() { // eslint-disable-next-line max-len it('register success', function(done) { request(url) .post('/api/v1/auth/register') .send(user) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'User registered successful' }); if (err) throw err; done(); }); }); it('repeated registration failure.', function(done) { request(url) .post('/api/v1/auth/register') .send(user) .expect(500) .end(function(err, res) { res.body.should.containEql({ err: 'REGISTER_FAILURE' }); if (err) throw err; done(); }); }); }); });
登陆接口的测试
没什么好讲的,同测试注册接口步骤一致
describe('UserLogin', function() { describe('POST /login', function() { it('login success', function(done) { request(url) .post('/api/v1/auth/login') .send(user) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'Authentication Success' }); if (err) throw err; done(); }); }); it('USER_NOT_EXIST.', function(done) { request(url) .post('/api/v1/auth/login') .send({ username: 'a', password: 'admin' }) .expect(200) .end(function(err, res) { res.body.should.containEql({ err: 'USER_NOT_EXIST' }); if (err) throw err; done(); }); }); }); });
权限验证的测试
1.权限验证就有点不同了,由于验证权限前必须先登陆,这时候就要用上mocha的钩子2.权限是经过cookie验证,因此验证前必需要带上cookie3.在before
钩子中加入
userCookie = res.header['set-cookie'];
4.在断言的请求中带上Cookie
.set('Cookie', userCookie)
// 权限验证describe('UserAuthInfo', function() { describe('GET /api/v1/auth/', function() { // 没有登陆,权限验证 it('The current User was not login.', function(done) { request(url) .get('/api/v1/auth/') .set('Accept', 'application/json') .expect('Content-Type', /json/) .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'NOT_LOGIN' }); if (err) throw err; done(); }); }); // 权限验证前先登陆 beforeEach(function(done) { request(url) .post('/api/v1/auth/login') .send(user) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('The username of the current user.', function(done) { request(url) .get('/api/v1/auth/') .set('Cookie', userCookie) .expect(200) .end(function(err, res) { res.body.should.have.keys('username'); if (err) throw err; done(); }); }); });});
用户注销接口的测试
没什么好讲的,同测试权限验证步骤一致
describe('UserLogout', function() { describe('GET /logout', function() { // 没有登陆,测试注销 it('NOT_LOGIN.', function(done) { request(url) .get('/api/v1/auth/logout') .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'NOT_LOGIN' }); if (err) throw err; done(); }); }); // 注销成功前先登陆 beforeEach(function(done) { request(url) .post('/api/v1/auth/login') .send(user) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('logout successful.', function(done) { request(url) .get('/api/v1/auth/logout') .set('Cookie', userCookie) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'logout successful' }); if (err) throw err; done(); }); }); }); });
更改用户密码的测试
更改用户密码前先注册-登陆
// 测试更改用户密码接口 describe('UserChangePassword', function() { describe('POST /changepassword', function() { // 更改用户密码前先注册-登陆 // eslint-disable-next-line no-undef before(function(done) { request(url) .post('/api/v1/auth/register') .send(user2) .end(function(err, res) { if (err) throw err; done(); }); }); // eslint-disable-next-line no-undef before(function(done) { request(url) .post('/api/v1/auth/login') .send(user2) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('change password successful', function(done) { request(url) .post('/api/v1/auth/changepassword') .set('Cookie', userCookie) .send(newUser2) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'change password successful' }); if (err) throw err; done(); }); }); it('AUTHENTICATE_FAILURE', function(done) { request(url) .post('/api/v1/auth/changepassword') .set('Cookie', userCookie) .send(newUser2) .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'AUTHENTICATE_FAILURE' }); if (err) throw err; done(); }); }); // after(function(done) { // request(url) // .post('/api/v1/auth/login') // .send(user22) // .set('Accept', 'application/json') // .end(function(err, res) { // if (!err) { // userCookie = res.header['set-cookie']; // done(); // } // }); // }); // after(function(done) { // request(url) // .post('/api/v1/auth/changepassword') // .set('Cookie', userCookie) // .send(oldUser2) // .expect(200) // .end(function(err, res) { // res.body.should.containEql({ // message: 'rechange password successful' // }); // if (err) throw err; // done(); // }); // }); }); });
问题是我改完后得将密码改回来,这一步我没有成功,很奇怪为何?
目前得每次测试完后将新旧密码调换,真的很麻烦
删除用户的测试
没什么好讲的,同测试权限验证步骤一致
describe('UserDelete', function() { describe('DELETE /user/:username', function() { it('NOT_LOGIN.', function(done) { request(url) .delete(`/api/v1/auth/user/${user.username}`) .expect(401) .end(function(err, res) { res.body.should.containEql({ err: 'NOT_LOGIN' }); if (err) throw err; done(); }); }); // 删除用户前先登陆 beforeEach(function(done) { request(url) .post('/api/v1/auth/login') .send(user) .set('Accept', 'application/json') .end(function(err, res) { if (!err) { userCookie = res.header['set-cookie']; done(); } }); }); it('delete user success', function(done) { request(url) .delete(`/api/v1/auth/user/${user.username}`) .set('Cookie', userCookie) .expect(200) .end(function(err, res) { res.body.should.containEql({ message: 'Delete User Successful' }); if (err) throw err; done(); }); }); }); });
未完待续
口渴了,快去喝杯mocha吧
参考文献
mocha官网[1]
测试框架 Mocha 实例教程[2]
前端测试框架对比(js单元测试框架对比)[3]
mocha 和 supertest[4]
使用mocha给mongoose写单元测试[5]
nodejs使用mocha进行接口测试[6]
node中使用 mocha + supertest + should 来写单元测试[7]
【Node开发笔记】单元测试工具Mocha和SuperTest[8]
一步一步搭建react应用-node中使用 mocha + supertest + should 来写单元测试[9]
mocha + chai + supertest 测试 node server[10]
Should.js[11]
接口自动化 开源框架学习-supertest[12]
supertest[13]
Nodejs单元测试小结[14]
使用mocha进行单元测试[15]
References
[1]
mocha官网: https://mochajs.org/[2]
测试框架 Mocha 实例教程: http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html[3]
前端测试框架对比(js单元测试框架对比): https://www.cnblogs.com/lihuanqing/p/8533552.html[4]
mocha 和 supertest: https://github.com/nswbmw/N-blog/blob/master/book/4.14%20%E6%B5%8B%E8%AF%95.md#4141-mocha-%E5%92%8C-supertest[5]
使用mocha给mongoose写单元测试: http://ju.outofmemory.cn/entry/86908[6]
nodejs使用mocha进行接口测试: https://blog.csdn.net/weixin_34308389/article/details/87441278[7]
node中使用 mocha + supertest + should 来写单元测试: https://github.com/xiyuyizhi/movies/blob/master/dayByday/day5.md[8]
【Node开发笔记】单元测试工具Mocha和SuperTest: https://www.imooc.com/article/2631[9]
一步一步搭建react应用-node中使用 mocha + supertest + should 来写单元测试: https://segmentfault.com/a/1190000011095213[10]
mocha + chai + supertest 测试 node server: https://webfem.com/post/mocha-test[11]
Should.js: http://shouldjs.github.io/[12]
接口自动化 开源框架学习-supertest: https://blog.csdn.net/lichao330530/article/details/51907075[13]
supertest: https://www.npmjs.com/package/supertest[14]
Nodejs单元测试小结: https://segmentfault.com/a/1190000002921481?utm_source=tag-newest[15]
使用mocha进行单元测试: https://www.jianshu.com/p/47575895bc54
本文分享自微信公众号 - 全栈大佬的修炼之路(gh_7795af32a259)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。