前言:
我属于TDD的拥护者,之前写后端代,都喜欢把一些重要的逻辑代码抽离处理,增加Unit Test。可是最近在接触前端Unit Test时,让自己忍不住想吐槽一下。也顺便实现一下今年的目标(一个月至少一篇技术博客,1月2月我会补上的)。前端测试框架有很多,比较流量的是Qunit和Jasminejs,这两个多单元测试框架的具体区别网上有很多资料,有兴趣大家可以去查一下。我是因为Jasminejs比较丰富的断言、断言扩展、spy、mock这些功能吸引着我。所以首先把jasminejs引入项目中。当时主要引入的是1.3版本。
最近在学习2.0版本,发现引入2.0版本的jasminejs代码,我的整个Unit Test跑不起来了。尽然不向下兼容?不兼容的问题让我顿时觉得jasminejs有点小儿科的感觉。幸亏做了一些封装,不然我是没办法升级了。
现在网上关于jasminejs的介绍大多数是针对1.3这个版本,下面是我排查和总结的经验,为自己做个笔记,也和大家分享一下。有错误地方请大家拍砖。
本文只针对1.3和2.0区别做说明,至于jasminejs详细学习手册建议大家上官网,介绍的很详细。
jasminejs:http://jasmine.github.io/
jasminejs github: https://github.com/pivotal/jasmine/
区别:
1、calls区别:
测试代码:
describe("对spy函数的测试", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; spyOn(foo, 'setBar'); //foo为spy函数 foo.setBar(123); foo.setBar(456, 'another param'); }); it("上一次被调用的参数", function() { var most = foo.setBar.calls; expect(most).toBeDefined(); }); });
1.3版本:
如图,chrome跟踪结果。
calls是一个数组,返回每次调用的上下文。数组最后一个值和mostRecentCall值相等。
expect(foo.setBar.calls[foo.setBar.calls.length-1]).toEqual(foo.setBar.mostRecentCall);
2.0版本:
如图,chrome跟踪结果。
calls是一个CallTracker的实例对象。这样更规范一些,用类封装了调用相关的信息。类中所有方法说明见下面。
2、1.3 mostRecentCall和2.0.calls.mostRecent()区别
(1)、调用方式不同
前者是:被调用方法.mostRecentCall,例如:foo.setBar.mostRecentCall
后者是:被调用方法.calls.mostRecent,例如:foo.setBar.calls.mostRecent()
(2)、值相同
mostRecentCall是一个对象,有两个属性:args和object,其中args是调用方法是所传的参数,object是调用方法的所属对象。
describe("对spy函数的测试", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; spyOn(foo, 'setBar'); //foo为spy函数 foo.setBar(123); foo.setBar(456, 'another param'); }); it("上一次被调用的参数", function() { expect(foo.setBar.mostRecentCall.args[0]).toEqual(456); }); it("上一次被调用的参数", function() { expect(foo.setBar.mostRecentCall.object === foo).toEqual(true); }); });
.calls.mostRecent()是函数,返回一个对象,同上。
3、2.0中强大的calls对象
.calls.any() 至少一次被调用,返回false/true。
.calls.count()被调用次数
.calls.argsFor(index)返回被index+1次调用的参数,返回值为[]
it("tracks the arguments of each call", function(){ foo.setBar(123); foo.setBar(456, "baz"); expect(foo.setBar.calls.argsFor(0)).toEqual([123]); expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]); });
.calls.allArgs()返回所有被调用的参数,以逗号隔开,每一次调用的参数,以数组[]的形式返回
.calls.all()同1.3中calls。返回每次调用的上下文context。
.calls.mostRecent()同1.3的mostRecentCall,返回最近一次调用的上下文信息。等同于.calls.all()[.calls.call().length - 1]
.calls.first等同于.calls.all()[0]
.calls.reset()重置spy的所有信息,此时.call.any()返回false。
4、异步啊异步
1.3为了实现异步测试,jasminejs使用了一种很初级的方法,循环等待。
describe("Asynchronous specs", function() { var value, flag; it("should support async execution of test preparation and expectations", function() { runs(function() { flag = true; value = 0; setTimeout(function() { flag = true; }, 500); }); waitsFor(function() { value++; return flag; }, "The Value should be incremented", 450); runs(function() { expect(value).toBeGreaterThan(0); }); }); });
这里的异步有以下几个问题:
1、写法太过复杂,需要自己封装;
2、实现有点初级。
waitsFor有三个参数:
第1个参数:校验异步是否完成函数,返回值为bool,返回true,停止循环等待;
第2个参数:错误提示,如果操作第3个参数指定的毫秒数,第1个参数还是返回false,断言失败。提示信息如下:
第3个参数:循环等待的时间,单位:毫秒。
异步实现,大多是ajax请求,这个请求受网络、服务器环境等诸多因素影响,所以很难估算需要多长时间。
2.0对这个async test做了很大的优化,也更合理了。
describe("Asynchronous specs", function() { var value; beforeEach(function(done) { setTimeout(function() { value = 0; done(); }, 1); }); it("should support async execution of test preparation and expectations", function(done) { value++; expect(value).toBeGreaterThan(0); done(); }); });
采用了一种pub/sub订阅模式处理,ajax请求成功,通知it执行。
5、仅仅写法不同
下面主要是功能一样,但是写法发生变化。
1.3 | 2.0 | 说明 |
andCallThrough | .and.callThrough | 函数监视器,但函数真的执行 |
andReturn | .and.returnValue | 函数监视器,函数不真的执行。指定监视的函数的返回值 |
andCallFake | .and.callFake | 替代被监视的函数,原函数不执行 |
calls | .calls.all() | 返回每次调用的上下文context |
mostRecentCall | .calls.mostRecent() | 最近一次被调用的上下文信息 |
jasmine.Clock.useMock | jasmine.clock().install | 启动模拟时间 |
jasmine.Clock.tick | jasmine.clock().tick | 模拟向前毫秒 |
jasmine.clock().uninstall() | 停止模拟时间,1.3无此方法 |
先写这些,其他区别如果有必要,我会在后续的文章中补充。没有必要我会加一些jasmine2.0的源码分析文章。