有谁能教下我下怎么用mocha进行mocha单元测试试

为了账号安全,请及时绑定邮箱和手机
【Node开发笔记】单元测试工具Mocha和SuperTest
【Node开发笔记】单元测试工具Mocha和SuperTest
毕业季,一堆乱七八糟的事情搞得已经整整两个月没有时间写写笔记了。最近进行着一个Node小项目,其中关于单元测试部分花了不少时间去踩坑。所以在这里简单总结一下关于Node开发测试框架Mocha(摩卡)的一些基础用法。
由于本人是入门级别的Node开发者,所以这篇文章也是仅适用于和我一样的初级Node开发者,如有不正确的地方欢迎大神们指正!
Mocha是非常流行JavaScript测试框架之一,在浏览器和Node环境都可以使用。这里主要是针对Node环境。
关于Mocha的安装当然是万能的npm了,直接npm install就OK,这里不再罗嗦了。
然后就是Mocha的语法了。首先来看一下最简单例子:
// 这是一个简单的加法函数
function add(a, b){
return a+b;
module.exports =
现在用Mocha写一个测试脚本去测试这个函数。一般来说,测试脚本的命名要和测试的模块一致,后缀为.test.js。这里测试脚本的名称为add.test.js,表示对add模块进行测试。
//add.test.js
var add = require('./add');
var should = require('should');
describe('test add', function() {
it('1 + 1 should be equal to 2', function(done){
(add(1,1) === 2).should.be.
然后保存代码之后进入终端,执行mocha add.test 即可。其中describe()函数是测试描述,表示一组相关测试用例是对哪个模块的测试。it()是一个测试用例,在一个describe块的内部可以执行对个测试用例(it块)。
这是同步函数的测试。在Node环境中,绝大部分的业务逻辑都是异步的,所以测试结果的回调是JavaScript测试框架需要解决的首要问题。Mocha提供了一个回调函数done,在业务代码执行完毕之后调用done()结束测试用例,不然的话测试用例就会出现timeout的情况导致测试用例失败。Mocha默认的超时时间是2000毫秒,如果由于测试用例的执行时间比较长需要延长超时时间,可以在命令行添加 -t 参数,比如mocha -t 3000 add.test.js。当然还有其他命令行参数,比如 mocha -w 用来监视指定的测试脚本。只要测试脚本有变化,就会自动运行Mocha。查看更多的命令行参数可以通过 mocha -h 查找。
那么接下来看一个异步测试例子:
describe('test async function', function(){
it('supertest example', function(done){
request.post('/')
.expect(200)
.end(function(err, res) {
should.not.exist(err);
在request请求执行完毕end()之后,一定要调用done(),否则测试用例超时,那么这个用例就失败了。当然这里用到了断言库should,稍后再介绍它的语法。
3、钩子(hooks)
Mocha的Hooks主要是用来设置前置条件和后置处理。比如说一个业务请求之前必须先登录,那个登录操作就在before()里面完成。Mocha提供了before,after,beforeEach,afterEach四个钩子。
describe('hooks', function() {
before(function() {
// runs before all tests in this block
after(function() {
// runs after all tests in this block
beforeEach(function() {
// runs before each test in this block
afterEach(function() {
// runs after each test in this block
it('test',function(done){
//test here
如果说某个测试需要跳过,使用it.skip(),Mocha跳过这个用例而不影响其他it块;只测试某个测试用例,使用it.only(),Mocha只执行.only用例,无视其他it块。
二、断言库should
Mocha本身是不包含断言库的,所以我们需要自己选择断言库。should是一个很简单的、贴近自然语言的断言库。当然,Mocha是适配所有的断言库的,如果你喜欢其他的断言库比如expect之类的,你也可以把它包含进来使用。
should的语法非常贴近自然语言,简单易懂,常见的should断言如下:
// 全等,相当于===
(5).should.be.exactly(5)
// 对象存在
true.should.be.
'yay'.should.be.
(1).should.be.
({}).should.be.
false.should.not.be.
(5===5).should.be.true
(err === null).should.be.
// 相等,相当于 ==
({ foo: 'bar' }).should.eql({ foo: 'bar' });
[1,2,3].should.eql([1,2,3]);
// see next example it is correct, even if it is different types, but actual content the same
[1, 2, 3].should.eql({ '0': 1, '1': 2, '2': 3 });
(undefined + 0).should.be.NaN;
// 判断类型
user.should.be.type('object');
'test'.should.be.type('string');
// 构造函数的一个实例
.instanceof
user.should.be.an.instanceof(User);
[].should.be.an.instanceOf(Array);
should.not.exist(err)
//深度包含
.containDeep()
[[1],[2],[3]].should.containDeep([[3]]);
[[1],[2],[3, 4]].should.containDeep([[3]]);
[{a: 'a'}, {b: 'b', c: 'c'}].should.containDeep([{a: 'a'}]);
[{a: 'a'}, {b: 'b', c: 'c'}].should.containDeep([{b: 'b'}]);
// 抛出异常
.throw()和throwError()
(function(){
throw new Error('fail');
}).should.throw();
(function(){
throw new Error('fail');
}).should.throw('fail');
// http响应的头部包含
res.should.have.header('content-length');
res.should.have.header('Content-Length', '123');
// 包含或等价于
.containEql
({ b: 10 }).should.containEql({ b: 10 });
([1, 2, { a: 10 }]).should.containEql({ a: 10 });
三、SuperTest
单单使用Mocha和should就几乎可以满足所有JavaScript函数的单元测试。但是对于Node应用而言,不仅仅是函数的集合,比如一个web应用的测试。这时候就需要配合一个http代理,完成Http请求和路由的测试。
Supertest是一个HTTP代理服务引擎,可以模拟一切HTTP请求行为。Supertest可以搭配任意的应用框架,从而进行应用的单元测试。
首先,传入应用来实例化supertest,比如说实例化一个express:
var supertest = require('supertest'),
express = require('express');
var app = express();
var request = supertest(app);
接下来就可以在Mocha测试用例中直接使用request发出一个基于该web应用的http请求了。
如果需要设置数据,supertest的API提供了 .set 来设置,比如:
describe('GET /users', function(){
it('respond with json', function(done){
request(app)
.get('/user')
.set('Accept', 'application/json')
.expect(200)
.end(function(err, res){
should.not.exist(err);
res.text.should.containEql('success');
2、.expect()
.expect 是一个断言,上述测试代码在执行之后期望的状态码是200(OK)。如果接收到的数据为html页面,.expect('Content-Type', 'text/charset=utf-8')
.end 是执行一个request请求,在回调函数里面根据业务逻辑的返回数据做断言分析。
4、.send()
很多情况下,项目需要测试一个表单业务。supertest提供了.send()方法来发送表单域数据。比如一个登录模块,需要发送用户名密码:
describe('test login', function(){
it('login sucessfully', function (done) {
request.post('/user')
username: 'username',
password: '123456'
.end(function (err, res) {
should.not.exists(err);
5、.attach()
.attach方法主要用来测试文件上传,由于.send()只能上传文本域,所以关于multipart -file的上传需要通过附件来绑定。
request(app)
.post('/')
.field('name', 'my awesome avatar')
.attach('avatar', 'test/fixtures/homeboy.jpg')
持久化Cookie
在很多业务测试中,需要用户先登录才有权限执行操作。这个时候作为HTTP请求模拟,必须要可以保存一些Cookie数据,也就是Cookie的持久化。这一般有两种解决思路。
1、在 supertest 中,可以通过 var request = supertest.agent(app) 获取一个 agent 对象,这个对象的 API 跟直接在 superagent 上调用各种方法是一样的。这个request在被多次调用 get 和 post 之后,可以一路把 cookie 都保存下来。
var supertest = require('supertest');
var app = express();
var request = supertest.agent(app);
request.post('login').end(...);
// then ..
request.post('create_topic').end(...); // 此时的request中有用户登陆后的 cookie
2、通过.set(),在发起请求时,调用 .set('Cookie', 'a cookie string') 这样的方式。
var request = require('supertest');
request.post('login')
.end(function (err, res) {
userCookie = res.headers['Cookie']
// then ..
request.post('create_topic')
.set('Cookie', userCookie)
2、实战demo
为了更好地帮助理解SuperTest的强大功能,这里放上我的一个项目模块的测试demo。
项目源文件
首先,这是一个express应用的登录注册和用户信息修改模块,要测试的就是这个user模块。为了节省篇幅,这里只放上UserController和Router两个项目文件。
UserController.js
// 用户首页
exports.getUser = function (req, res) {
if (!req.user) {
res.send({status: false, info: '未登录'});
res.render('user', {status: true, userdata: req.user});
exports.login =
function (req, res, next) {
req.session.save(function (err) {
if (err) {
log.error(err);
return next(err);
res.redirect('/user/' + req.user.username);
exports.logout = function (req, res, next) {
req.logout();
req.session.save(function (err) {
if (err) {
log.error(err);
return next (err);
res.redirect('/user');
res.end();
// 用户注册
exports.addUser = function (req, res, next) {
if (!req.body.username || !req.body.password) {
return res.send({status: false, info: '用户名或密码不能为空'});
} else if (req.body.password.length & 6) {
return res.send({status: false, info: '密码长度太短'});
User.register(new User({username: req.body.username}), req.body.password, function (err, user) {
if (err) {
log.error(err);
return res.render('register', {info: '用户名已被使用'});
passport.authenticate('local')(req, res, function () {
req.session.save(function (err) {
if (err) {
log.error(err);
return next(err);
res.redirect('/user/' + req.body.username);
// 更新用户信息文字资料
exports.updateInfo = function (req, res, next) {
User.findOne({username: req.user.username}, function (err, doc) {
if (doc) {
doc.update(req.body, function (err, data) {
if (err) {
log.error(err);
return next(err);
res.send({status: true});
// 更换头像
exports.updateAvator = function (req, res,next) {
upload(req, res, function (err) {
if (err) {
log.error(err);
return next(err);
User.findOne({username: req.user.username}, function (err, doc) {
if (err) {
log.error(err);
return next(err);
} else if (doc) {
doc.update({avator: req.file.filename}, function (err, data) {
if (err) {
log.error(err);
return next(err);
res.type('html');
res.send({status: true, avator: req.file.filename});
return next();
Router文件
router.get('/:uid', isAuthenticated, user.getUser);
router.get('/', function (req, res) {
res.render('login', {info: ''});
router.post('/',passport.authenticate('local'), /*{failureRedirect: '/user'}),*/ user.login);
router.post('/authenticate/logout', user.logout);
router.get('/authenticate/register', function (req, res) {
res.render('register', {info: ''});
router.post('/authenticate/register', user.addUser);
// 用户相关的资料
router.post('/update/info', user.updateInfo);
router.post('/update/avator', user.updateAvator);
module.exports =
首先,测试注册功能。为了防止用户名冲突,在测试用例之前设置钩子before块,清除User集合。
// user.test.js
describe('test user.addUser()', function () {
before(function (done) {
User.remove({}, function (err) {
it('register sucessfully', function (done) {
request.post('/user/authenticate/register')
username: 'username',
password: '123456'
//注册成功之后重定向
.expect(302, function (err, res) {
should.not.exists(err);
//测试用户名存在的用例
it('username already exist', function (done) {
request.post('/user/authenticate/register')
username: 'username',
password: '123456'
.end(function (err, res) {
should.not.exists(err);
res.text.should.containEql('用户名已被使用');
// 测试头像上传
describe('test user.updateAvator()', function () {
//bofore HOOKS,测试业务逻辑之前先登录,并持久化cookie
before(function (done) {
request.post('/user')
username: 'username',
password: '123456'
.end(function (err, res) {
it('update avator upload sucessfully', function (done) {
request.post('/user/update/avator')
.attach('avator','test/image/test.jpg')
.end(function (err, res) {
should.not.exists(err);
res.text.should.containEql('"status":true');
分享即可 +1积分
请登录后,发表评论
评论(Enter+Ctrl)
评论加载中...
评论加载中...
Web前端工程师
作者的热门手记
Copyright (C)
All Rights Reserved | 京ICP备 号-2如何使用Mocha跟踪较难跟踪的画面---中文字幕教程
mocha的Martin Brennand
在这个教学影片中,示范如何在较难跟踪的画面进行跟踪,这是一个跟踪难度较高的教程,在平面跟踪的mocha
進行摄像机轨迹的运算,显的十分容易。
教程下载地址:在上传中
________________________________________________________________________
________________________________________________________________________
如需转载请注明:转载自书生博客
您的支持是我继续下去的动力,请支持原创文章
更多资源分享,请关注
书生的新浪博客:
书生的新浪围脖:
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。后使用快捷导航没有帐号?
只需一步,快速开始
查看: 8171|回复: 97
CG高手, 积分 405, 距离下一级还需 795 积分
未标题-.jpg (22.82 KB, 下载次数: 32)
09:08 上传
解压密码(请回复后查看)
游客,如果您要查看本帖隐藏内容请
本资源为迅雷快传网盘下载!可能会过期失效!请尽快下载!
扩散点击排行榜(↑↑↑ 轻松快速-免费获得积分-最快方法-无论扩散哪个帖子都可以拿积分哦 ↑↑↑):(12)(1)
& 积分提示:活力值可以兑换银子用于下载哦!& &&快去推广吧!版主亲测分分钟几百个活力值
12:46 上传
点击文件名下载附件
7.27 KB, 下载次数: 50
售价: 6 两银子 &[]
活力值 +10
感谢分享!
~~~还没有人打赏~~~
蛮不错的!感谢楼主分享!
CG预备役, 积分 0, 距离下一级还需 30 积分
谢谢分享!
CG乞丐, 积分 -2679, 距离下一级还需 2679 积分
感谢楼主,来看一下。
CG新人, 积分 33, 距离下一级还需 267 积分
感谢楼主分享。
CG新人, 积分 64, 距离下一级还需 236 积分
CG预备役, 积分 3, 距离下一级还需 27 积分
看看,好像很不错的样子
CG预备役, 积分 21, 距离下一级还需 9 积分
看看,学习一下
CG预备役, 积分 19, 距离下一级还需 11 积分
谢谢啊。。。。。跪求密码
CG预备役, 积分 0, 距离下一级还需 30 积分
支持!!!!!!!!
Powered by}

我要回帖

更多关于 vs2013单元测试教程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信