原文: http://mherman.org/blog/2015/09/10/testing-node-js-with-mocha-and-chai/#.ViO8oBArIlJ

为什么要测试?

在此之前了解为什么要测试非常重要.

通过下面的地址获取一个Node/Express的简单的CRUD应用:

$ git clone https://github.com/mjhea0/node-mocha-chai-tutorial.git
$ git checkout tags/v1

现在可以通过食用curl(或者Postman)来测试CRUD了:

  1. 添加一个新的blob
  2. 查看所有的blob
  3. 查看一个blob
  4. 更新一个blob
  5. 删除一个blob

这个过程十分乏味. 如果我们在这个应用中没添加一个新功能就需要手动做这样的测试, 不仅会占用大量的时间而且可能还会因为手误或忘记测试某个功能使得测试不可信. 所以我们需要自动测试, 有了自动测试我们可以在几秒钟内测试玩几百个这样的功能.

首先, 安装Mocha:

$ npm install -g mocha@2.3.1

结构

在应用的根目录创建一个文件夹test, 然后在这个test文件夹中创建一个test-server.js. 现在项目的结构如下:

├── package.json
├── server
│ ├── app.js
│ ├── models
│ │ └── blob.js
│ └── routes
│ └── index.js
└── test
└── test-server.js

现在, 在test-server.js中添加下面的代码:

describe('Blobs', function() {
it('should list ALL blobs on /blobs GET');
it('should list a SINGLE blob on /blob/<id> GET');
it('should add a SINGLE blob on /blobs POST');
it('should update a SINGLE blob on /blob/<id> PUT');
it('should delete a SINGLE blob on /blob/<id> DELETE');
});

describe用来分组测试. it语句包含了每个独立的测试用例.

逻辑

Chai是一个assertion库, 我们使用chai-http做一些HTTP请求, 然后测试响应:

使用下面的命令同时安装chai和chai-http:

$ npm install chai@3.2.0 chai-http@1.0.0 --save-dev

更新test-server.js如下:

var chai = require('chai');
var chaiHttp = require('chai-http');
var server = require('../server/app');
var should = chai.should(); chai.use(chaiHttp); describe('Blobs', function() {
it('should list ALL blobs on /blobs GET');
it('should list a SINGLE blob on /blob/<id> GET');
it('should add a SINGLE blob on /blobs POST');
it('should update a SINGLE blob on /blob/<id> PUT');
it('should delete a SINGLE blob on /blob/<id> DELETE');
});

现在可以写测试了

测试-GET(all)

更新第一个it():

it('should list ALL blobs on /blobs GET', function(done) {
chai.request(server)
.get('/blobs')
.end(function(err, res){
res.should.have.status(200);
done();
});
});

我们传递了一个只用一个参数(done function)的匿名函数给it. 这个匿名函数的最后一行是done(). 这个测试十分简单, Get /blobs 判断响应的http status code是否是200.

简单的运行mocha就能测试了:

$ mocha

  Blobs
Connected to Database!
GET /blobs 200 19.621 ms - 2
✓ should list ALL blobs on /blobs GET (43ms)
- should list a SINGLE blob on /blob/<id> GET
- should add a SINGLE blob on /blobs POST
- should update a SINGLE blob on /blob/<id> PUT
- should delete a SINGLE blob on /blob/<id> DELETE 1 passing (72ms)
4 pending

因为简单的判断HTTP状态码对我们判断是否返回整个blobs来说意义不大, 现在来添加更多的断言:

it('should list ALL blobs on /blobs GET', function(done) {
chai.request(server)
.get('/blobs')
.end(function(err, res){
res.should.have.status(200);
res.should.be.json;
res.body.should.be.a('array');
done();
});
});

  

测试-POST

当添加blob成功, 响应如下:

{
"SUCCESS": {
"__v": 0,
"name": "name",
"lastName": "lastname",
"_id": "some-unique-id"
}
}

现在我们来写测试:

it('should add a SINGLE blob on /blobs POST', function(done) {
chai.request(server)
.post('/blobs')
.send({'name': 'Java', 'lastName': 'Script'})
.end(function(err, res){
res.should.have.status(200);
res.should.be.json;
res.body.should.be.a('object');
res.body.should.have.property('SUCCESS');
res.body.SUCCESS.should.be.a('object');
res.body.SUCCESS.should.have.property('name');
res.body.SUCCESS.should.have.property('lastName');
res.body.SUCCESS.should.have.property('_id');
res.body.SUCCESS.name.should.equal('Java');
res.body.SUCCESS.lastName.should.equal('Script');
done();
});
});

  

Hooks

专门有一个test数据库并且插入一些样例数据用来测试. 我们可以使用beforeEach和afterEach hooks-用来在每一个单元测试运行之前添加一些样例数据, 运行之后删除这些样例数据.

使用Mocha来做这些特别简单!

在server文件夹下面创建一个配置文件_config.js, 使用它为测试指定一个不通的数据库uri:

var config = {};

config.mongoURI = {
development: 'mongodb://localhost/node-testing',
test: 'mongodb://localhost/node-test'
}; module.exports = config;

接下来更新app.js文件:

// *** config file *** //
var config = require('./_config'); // *** mongoose *** ///
mongoose.connect(config.mongoURI[app.settings.env], function(err, res) {
if(err) {
console.log('Error connecting to the database. ' + err);
} else {
console.log('Connected to Database: ' + config.mongoURI[app.settings.env]);
}
});

最后更新测试脚本:

process.env.NODE_ENV = 'test';

var chai = require('chai');
var chaiHttp = require('chai-http');
var mongoose = require("mongoose"); var server = require('../server/app');
var Blob = require("../server/models/blob"); var should = chai.should();
chai.use(chaiHttp); describe('Blobs', function() { Blob.collection.drop(); beforeEach(function(done){
var newBlob = new Blob({
name: 'Bat',
lastName: 'man'
});
newBlob.save(function(err) {
done();
});
});
afterEach(function(done){
Blob.collection.drop();
done();
}); ...snip...

现在, 每个单元测试(it)运行之前数据库会被清空, 接下来会添加一个blob. 运行之后有会清空数据库.

测试-GET(all)

有了hooks的设置后, 我们修改get(all)的单元测试:

it('should list ALL blobs on /blobs GET', function(done) {
chai.request(server)
.get('/blobs')
.end(function(err, res){
res.should.have.status(200);
res.should.be.json;
res.body.should.be.a('array');
res.body[0].should.have.property('_id');
res.body[0].should.have.property('name');
res.body[0].should.have.property('lastName');
res.body[0].name.should.equal('Bat');
res.body[0].lastName.should.equal('man');
done();
});
});

测试-GET(single)

it('should list a SINGLE blob on /blob/<id> GET', function(done) {
var newBlob = new Blob({
name: 'Super',
lastName: 'man'
});
newBlob.save(function(err, data) {
chai.request(server)
.get('/blob/'+data.id)
.end(function(err, res){
res.should.have.status(200);
res.should.be.json;
res.body.should.be.a('object');
res.body.should.have.property('_id');
res.body.should.have.property('name');
res.body.should.have.property('lastName');
res.body.name.should.equal('Super');
res.body.lastName.should.equal('man');
res.body._id.should.equal(data.id);
done();
});
});
});

测试-PUT

it('should update a SINGLE blob on /blob/<id> PUT', function(done) {
chai.request(server)
.get('/blobs')
.end(function(err, res){
chai.request(server)
.put('/blob/'+res.body[0]._id)
.send({'name': 'Spider'})
.end(function(error, response){
response.should.have.status(200);
response.should.be.json;
response.body.should.be.a('object');
response.body.should.have.property('UPDATED');
response.body.UPDATED.should.be.a('object');
response.body.UPDATED.should.have.property('name');
response.body.UPDATED.should.have.property('_id');
response.body.UPDATED.name.should.equal('Spider');
done();
});
});
});

测试-删除

it('should delete a SINGLE blob on /blob/<id> DELETE', function(done) {
chai.request(server)
.get('/blobs')
.end(function(err, res){
chai.request(server)
.delete('/blob/'+res.body[0]._id)
.end(function(error, response){
response.should.have.status(200);
response.should.be.json;
response.body.should.be.a('object');
response.body.should.have.property('REMOVED');
response.body.REMOVED.should.be.a('object');
response.body.REMOVED.should.have.property('name');
response.body.REMOVED.should.have.property('_id');
response.body.REMOVED.name.should.equal('Bat');
done();
});
});
});

  

[译]Testing Node.js With Mocha and Chai的更多相关文章

  1. [Node.js] Testing ES6 Promises in Node.js using Mocha and Chai

    Writing great ES6 style Promises for Node.js is only half the battle. Your great modules must includ ...

  2. 【译】 Node.js v0.12的新特性 -- 性能优化

    原文: https://strongloop.com/strongblog/performance-node-js-v-0-12-whats-new/ January 21, 2014/in Comm ...

  3. 【译】 Node.js v0.12的新特性 -- Cluster模式采用Round-Robin负载均衡

    原文:https://strongloop.com/strongblog/whats-new-in-node-js-v0-12-cluster-round-robin-load-balancing 本 ...

  4. (译+注解)node.js的C++扩展入门

    声明:本文主要翻译自node.js addons官方文档.部分解释为作者自己添加. 编程环境: 1. 操作系统 Mac OS X 10.9.51. node.js v4.4.22. npm v3.9. ...

  5. 【nodejs原理&源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick

    [摘要] 官网博文翻译,nodejs中的定时器 示例代码托管在:http://www.github.com/dashnowords/blogs 原文地址:https://nodejs.org/en/d ...

  6. 【nodejs原理&源码赏析(7)】【译】Node.js中的事件循环,定时器和process.nextTick

    目录 Event Loop 是什么? Event Loop 基本解释 事件循环阶段概览 事件循环细节 timers pending callbacks poll阶段 check close callb ...

  7. 带你入门带你飞Ⅱ 使用Mocha + Chai + SuperTest测试Restful API in node.js

    目录 1. 简介 2. 准备开始 3. Restful API测试实战 Example 1 - GET Example 2 - Post Example 3 - Put Example 4 - Del ...

  8. Practical Node.js (2018版) 第3章:测试/Mocha.js, Chai.js, Expect.js

    TDD and BDD for Node.js with Mocha TDD测试驱动开发.自动测试代码. BDD: behavior-driven development行为驱动开发,基于TDD.一种 ...

  9. 10+ 最佳的 Node.js 教程和实例

    如果你正在找Node.js的学习资料及指南,那么请继续(阅读),我们的教程将会覆盖即时聊天应用.API服务编写.投票问卷应用.人物投票APP.社交授权. Node.js on Raspberry Pi ...

随机推荐

  1. redis 配置

    一 Redis 支持写的指令 Redis大概的命令如下:set setnx setex appendincr decr rpush lpush rpushx lpushx linsert lset r ...

  2. 微信公共号的PHP开发(基础篇)——玩一下

    最近没事儿开了个微信号,写点东西给家人啊什么的看,你们想看的话就这个嘛: 然后就意外的看到imooc上的微信公众号开发相关了.每天科研的累累的,做点这个不是很累的东西吧. 微信公共号开发 一.基础 1 ...

  3. 【原】迎接微信winphone 5.0 版本的IE10样式兼容

    微信 Android 5.1 和 iPhone 5.1 已正式发布了,据说本12月底,微信将推出 Winphone 5.0版本,全面支持微信支付,它绑定 IE10 浏览器,那么做微信公众号的 H5 页 ...

  4. openstack学习(三)创建虚拟机

    1 , 用图形管理界面创建虚拟机 #virt-manager    //打开图形管理界面 配置文件一般存放于/etc/libvirt/qemu 然后按照提示一步步安装即可,网络连接方式改为桥接 导出创 ...

  5. 洛谷U4859matrix[单调栈]

    题目描述 给一个元素均为正整数的矩阵,上升矩阵的定义为矩阵中每行.每列都是严格递增的. 求给定矩阵中上升子矩阵的数量. 输入输出格式 输入格式: 第一行两个正整数n.m,表示矩阵的行数.列数. 接下来 ...

  6. [tem]高精度1

    1 //倒着存 B取的低精最大值所以简化了一点 #include <iostream> #include <cstdio> #include <cstring> # ...

  7. Web API--自定义异常结果的处理

    1.常规的异常处理 统一的异常处理,把正确的信息返回给调用者很重要,可以让接口开发人员或者用户,了解具体的原因所在,这样可以得到有效的错误处理. 参考微信API的处理,微信API,对于调用都有一个错误 ...

  8. bash/shell编程学习(1)

    1)定义变量 myvar=abc #注:等号前后不能加空格 #或 myvar="abc" #或 myvar='abc' #注:如果变量后面的值中间本身没有空格,加不加引号都无所谓, ...

  9. WPF实现物理效果 拉一个小球

    一直以来都对物理效果有神秘感,完全不知道怎么实现的.直到看到了周银辉在老早前写的一篇博客:http://www.cnblogs.com/zhouyinhui/archive/2007/06/23/79 ...

  10. JS组件系列——Form表单验证神器: BootstrapValidator

    前言:做Web开发的我们,表单验证是再常见不过的需求了.友好的错误提示能增加用户体验.博主搜索bootstrap表单验证,搜到的结果大部分都是文中的主题:bootstrapvalidator.今天就来 ...