最近被Jasmine中的异步超时弄得崩溃了,具体问题就是setTimeout,有时导致测试用例超时,有时又没有等待足够的时间。因此下定决心研究一下Jasmine中异步超时的用法。
首先,看看jasmine文档给的例子:
describe("long asynchronous specs", function() {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
it("testTimeout", function(done) {
setTimeout(function() {
done();
}, 9000);
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
});
这个例子非常简单,运行testTimeout这个用例时也大约停顿了9秒,测试结果为:
SPEC HAS NO EXPECTATIONS testTimeout
这个结果还算正常,因为测试用例中没有任何测试期望,如果加上expect(1).toEqual(1);语句,就不会提示上面的信息了。
接下来,仿照上面的代码写了一个例子:
describe("TimeOutTest", function() {
var value = 0;
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("testTimeout", function(done) {
console.log("start testTimeout ...");
expect(value).toEqual(0);
setTimeout(function() {
console.log("timeout callback");
value++;
done();
}, 2000);
console.log("after timeout");
expect(value).toEqual(1);
});
});
但结果出乎意料之外,有两个错误:
- expect(value).toEqual(1); 这句failed,此时value的值为0。仔细一想,value值确实是0,因为setTimeout函数虽然设置了超时回调,但这个函数本身并不会阻塞在这儿,而是会接着往下执行。
- 提示错误:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
这个错误就有些不理解了,和上一个例子的代码差不多,为什么这个不会回调到超时callback呢?如果回调了,执行了done(); 就不会出现上述的超时错误。
再研究jasmine的例子,将上述的例子修改如下:
describe("TimeOutTest", function() {
var value = 0;
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("testTick", function(done) {
console.log("start testTick ...");
expect(value).toEqual(0);
setTimeout(function() {
console.log("timeout callback");
value++;
done();
}, 2000);
console.log("after timeout");
jasmine.clock().tick(2001);
expect(value).toEqual(1);
});
});
这次测试用例运行正确,秘诀就在于引入了jasmine.clock().tick()函数,这个函数运行会有两个后果:
- 它会阻塞javascript代码的执行,相当于C程序中的sleep函数。
- 超时之后回调到setTimeout中的设置的callback,在上面的例子中,会执行到value++,所以expect(value).toEqual(1);这句测试得以通过。
再回头看第二个例子,问题出在哪儿呢?原因在于jasmine.clock().install();这句话,它实际上会改写系统的setTimeout函数,它将会同步等待一段时间过去,这个时候就需要借助于jasmine.clock().tick()来休眠一段时间。如果没有这句话,测试用例会等待,直到jasmine.DEFAULT_TIMEOUT_INTERVAL超时。如果第三个例子中jasmine.clock().tick(2001);改为小于2000的一个值,会是什么情况呢?答案就是出现jasmine.DEFAULT_TIMEOUT_INTERVAL超时,也就是等待的时间不够长,不会触发setTimeout超时。
总结:
- jasmine.clock().install()/jasmine.clock().uninstall()要和jasmine.clock().tick()配合使用。
以上的完整代码可参考我在github上的项目jsdemo.