1, Sinon.js 是一个用于JS测试的辅助工具,它提供了三种类型的fake对象:spies, stubs and mocks, fake对象是测试工具集的一个非常有用的部分.
Christian Johansen——Sinon.JS的创建者,解释了为什么你需要fake。在Javascript中,这些原因可以分为:
性能--真实的DOM操作,依靠定时行为及网络活动减慢了测试
隔离--单元测试应把重点放在小的一块功能成为可能,并解耦不可靠的或低依赖
Spies 监视一个函数被调用的情况,记录它的参数,返回值和异常,spy可以是匿名函数,也可以跟踪现有函数.
Stubs 可以预设函数的行为,除了具有spy所有的功能之外,它还可以改变函数的行为.stub可以是匿名函数,也可以包装现有函数. 当包装现有函数时,原始函数是不会被调用的.
Mocks 用于对一个object的活动进行监视,一个object被mock以后,就可以设定我们对这个object有怎样的预期。具有spies和stubs的所有功能.
2, Nock 是基于Node.js的http 模拟请求库.可以隔离测试模块的http请求. 也就是说它只具备Sinon.js的一小部分功能,但是用起来比较方便.
我们基于上一篇文章的内容继续.
npm i sinon nock --save-dev
我们将文章内容存在以ID全名的json文件里,保存在articls目录中.
test
articles --
10001.json
10002.json
app.js
在app.js中新增获取文章列表的API.
//获取文章列表
app.get('/articleList',function (req,res) {
var articles = [];
fs.readdirSync('./articles').forEach(function (file) {
articles.push(JSON.parse(fs.readFileSync('./articles/' + file, 'utf8')));
});
return res.status(200).json(articles);
});
新建测试文件: test/articles.test.js
'use strict';
var app = require('../app.js');
var should = require('should');
var request = require('supertest')(app);
var sinon = require('sinon');
var fs = require('fs');
describe('/articleList', function() {
it('should return article list',function (done) {
var spydir = sinon.spy(fs, "readdirSync");
var spyfile = sinon.spy(fs,"readFileSync")
request.get('/articleList')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
.end(function (err,res) {
if (err) return done(err);
spydir.calledOnce.should.be.true();
spyfile.callCount.should.be.above(1);
res.body.should.be.Array();
fs.readdirSync.restore();
fs.readFileSync.restore();
done();
});
});
it('should return article list length three',function (done) {
var mockData = {'id':'10003','title':'中国人加油','content':'油加满了有点晃.'};
var stubdir = sinon.stub(fs, "readdirSync");
stubdir.returns(['10003.json','10004.json','10005.json']);
var stubfile = sinon.stub(fs,"readFileSync");
stubfile.returns(JSON.stringify(mockData));
request.get('/articleList')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
.end(function (err,res) {
if (err) return done(err);
stubdir.calledOnce.should.be.true();
stubfile.callCount.should.be.equal(3);
res.body.should.be.Array();
res.body.length.should.be.equal(3);
stubdir.restore();
stubfile.restore();
done();
});
});
});
在第一个用例中我们通过sinon的spy 来监听fs的readdirSync和readFileSync的调用情况.
这时是真实的读取了文件夹里的文件,readdirSync只读取了articles一个文件夹,也就是只调用了一次,而readFileSync读取的文章会有多个文件夹,也就是大于一次.
在第二个用例中我们通过sinon的stub打桩,实际上是没有真实的是读取文件,隔离了文件读取操作.这里指定了三个文件,最后期望匹配stubFile调用了3次,返回结果数组长度是3.
运行测试npm test 测试通过,说明监听结果是正确的.
在真实项目中不可避免的会有第三方网络调用,比如第三方登录,七牛图片存取等等. 而这些网络请求比较慢,而且不稳定.而我们测试的重点在于模块的功能.所以用nock来模拟请求并返回数据,解除依赖,方便测试.
安装supertest的兄弟superagent
npm i superagent --save
增加一个通过IP地址获取真实地址的API:
var superagent = require('superagent');
//获取用户地址
app.get('/userAddress',function (req,res) {
var ip = ((/127\.0\.0\.1/).test(req.ip) || req.ip === '::1')?'210.75.225.254':req.ip;
var url = "http://ip.taobao.com/service/getIpInfo.php?ip=" + ip;
superagent.get(url)
.set('Accept', 'application/json')
.end(function (err,response) {
var infos = JSON.parse(decodeURI(response.text));
if(infos.code === 0){
return res.status(200).json(infos.data);
}else{
return res.status(500).send();
}
});
});
新建测试文件: test/useraddress.test.js
'use strict';
var app = require('../app.js');
var should = require('should');
var supertest = require('supertest')(app);
var request = require('request');
var sinon = require('sinon');
var fs = require('fs');
var nock = require('nock');
describe('/userAddress',function () {
it('should resturn success',function (done) {
nock('http://ip.taobao.com')
.get('/service/getIpInfo.php')
.query(true)
.reply(200, {"code":0,"data":{"country":"\u4e2d\u56fd","country_id":"CN","area":"\u534e\u5317","area_id":"100000","region":"\u5317\u4eac\u5e02","region_id":"110000","city":"\u5317\u4eac\u5e02","city_id":"110100","county":"","county_id":"-1","isp":"\u4e2d\u56fd\u79d1\u6280\u7f51","isp_id":"1000114","ip":"210.75.225.254"}});
supertest.get('/userAddress')
.expect('Content-Type', /json/)
.expect(200)
.end(function (err,res) {
if (err) return done(err);
res.body.city.should.be.ok();
res.body.country_id.should.be.equal('CN');
nock.restore();
done();
});
})
});
使用nock当对指定的URL请求时,便隔离真实的请求,而响应并返回数据,在API中的其它逻辑并不受影响,而我们的测试效率大提高了.
本文样例下载地址:https://github.com/jackhutu/test-example/tree/sinon-nock
我来逛逛啊 哈哈
hhh
嘻嘻
我也来逛~~
111
haha
水电费
jkjkjjjjkjk
niu qi
asdasd
学习了
666
123
333
参考一下
咳咳咳