测试笔记 (二) sinon and nock

阅读3959评论11

一,简介:

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);
});

三, 使用sinon的spy和stub

新建测试文件: 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

在真实项目中不可避免的会有第三方网络调用,比如第三方登录,七牛图片存取等等. 而这些网络请求比较慢,而且不稳定.而我们测试的重点在于模块的功能.所以用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

11条评论添加新评论
高阳阳高阳阳2016.3.16 3:0

我来逛逛啊 哈哈

Jack@高阳阳 欢迎

anrhkkanrhkk2016.5.23 10:0

hhh

wangyanwangyan2016.6.26 8:47

嘻嘻

szy0syzszy0syz2016.10.17 2:11

我也来逛~~

cssxncssxn2016.10.19 4:47

111

qiufengqiufeng2016.11.2 2:15

haha

joyc-fejoyc-fe2016.12.19 13:24

水电费

VictorYuan666VictorYuan6662017.10.19 6:17

jkjkjjjjkjk

CareyToboo123CareyToboo1232017.11.28 2:32

niu qi

wxmaudiowxmaudio2018.5.7 8:50

asdasd

Xiankun ChengXiankun Cheng2018.5.16 4:13

学习了