探知JS测试(2)
前一篇文章探知JS测试(1),我们已经简单的阐述了BDD,TDD以及mocha测试框架,chai断言库。这里我们将进一步深入,比较全部的了解测试的API。前文,我们已经知道了,BDD本身可以比拟为文章的骨架,而chai断言库就是骨架里面的血管和肌肉(脂肪)。 两者结合才能写出一片完美的测试文章。 这里,我们先看一下脂肪是怎么炼成的。
一、expect断言
这里我针对expect断言进行一些讲解,顺便把一些比较重要的API,梳理一遍。
逻辑指示语法:
not
deep
any
all
and
not
取非语法
expect(1).to.not.equal(2); //pass
deep
深层检查,该主要针对于对象和数组类型。 就好比复制一样,分为深复制和浅复制. 通常情况下,我们直接比较的都是浅层.像这样的
//浅层比较
var expect = require('chai').expect;
var test1 = {
foo:{
bar:1
}
}
var test2 = {
bar:1
}
describe('Counter',function(){
it('test',()=>{
expect(test1.foo).to.equal(test2);
})
})
//fail
//使用深层比较
describe('Test',function(){
it('test',()=>{
expect(test1.foo).to.deep.equal(test2);
})
})
//pass
any
按正常语法看,这里就是任意的意思。通常搭配keys一起使用,用来表示或的意思。
expect(test1).to.have.any.keys('foo','bar');
all
这个语法和"any"的使用也差不多,也是用来表示keys的包含状况
//test1对象的所有keys只有foo和bar
expect(test1).to.have.all.keys('foo',"bar");
上面的逻辑语法通常和动词语法一起使用.常用的动词语法有:
equal
include (alias: contain)
satisfy
match
change
通常,我们使用这里动词结合名词来表达一个状态
equal
顾名思义就是相等的意思呗。直接看一下demo
expect(1).to.equal(1);
//相等
expect(2).to.not.equal(3);
//不相等
include
包含的意思,其实他的alias还有contains,includes。 不过表达的都是一样的意思,所以推荐直接使用include就可以了。include经常和array,string,以及object使用. 正对于这3种类型,《包含》意味着:
array: 查询是否包含子项
string: 查询是否包含子字符串
object: 查询是否包含子对象
//查询array是否包含子项
expect([1,2,3,4,5]).to.include(3);
//包含sting的子串
expect("good").to.include("oo");
//判断对象是否包含子对象
expect({first:1,second:2}).to.include({second:2})
还记得上文提到的have吗? 其实,这个和include是有区别的。 一般而言,have通常是和keys进行搭配的.
expect({first:1,second:2}).to.have.any.keys("first");
//等价于
expect({first:1,second:2}).to.include.keys("first");
另外,have本身自带all的效果,所以感觉"all"通常是和include一起搭配.
expect({first:1,second:2}).to.have.keys("first","second");
//等价于
expect({first:1,second:2}).to.have.all.keys("first","second");
//等价于
expect({first:1,second:2}).to.include.all.keys("first","second");
satisfy
这个应该可以算是一个万能assertion. 他的参数是一个函数,接受的参数就是expect里面的内容。 而且该函数的返回值必须是Boolean. 如果你的返回值没有的话,那么该case是不会通过的。
expect({first:1,second:2}).to.satisfy(function(obj){
return obj.first===1;
});
//pass
虽然是万能的,但是极力不推荐使用,因为是在是太好用了,而造成的problem就是你的测试用例不清晰,没有很好的可读性。要知道断言库的出现就是让你的测试用例能够像文章一样具有良好的可读性.
match
这个方法其实和string.match是比较类似的。他们都是用来检测字符串类型,是否满足正则的测试。
expect("good").to.match(/(oo){1}/);
//pass
change
用来表征一个对象的值是否被一个函数所改变.
//测试对象
var test2 = {
bar:1
}
function changeValue(){
test2.bar=2;
}
//测试用例
expect(changeValue).to.change(test2,"bar");
事实上,这个真的没有什么用。用的频率还是蛮低的。
Okkkkay~
在断言中还有最重要的一部分,关于number的判断.
常用的number判断有:
above(alias: gt)
below(alias: lt)
least(alias: gte)
most(alias: lte)
within
above
above等价于greater than(>=). 但是above能更好的表达语义,所以这里就使用above.
expect(2).to.above(1);
below
同理below和above类似. below等价于less than(<=)
expect(2).to.not.below(1);
expect(2).to.below(3);
least
在中文意译就为至少为多少~ 翻译为数学语言就是 (>=) . 这里,我们需要明确一个概念,即,测试用例应该给你最直观的感受, 我们的用例是给normal people看的,而不是特指针对于数学家,或者程序员看的。使用最佳语义化的表达就是最重要的.
expect(num).to.least(1);
//翻译一遍就是: num至少为1
most
和least相似。我这里就不举例论证了。
expect(num).to.most(12);
//num最多为12
within
标识一个数的range. 相当于,数学上的 "[num1,num2]". 判断你的expect是否在这个区间内。
expect(1).to.within(1,2);
//pass
关于Number这一块内容我们差不多梳理完了。
是不是很枯燥呀~
恩~ 我也这么觉得
下面,我们再来看一点枯燥的东西呗。 破罐子破摔
类型判断
要知道,在js中,类型判断是最奇葩的一个。 针对于类型js已经出啦3个方法来判断》 instanceof typeof constructor. 在各种库里,也出现了想isArray,isBoolean,isArrayLike等语义API判断。 在断言库里,类型判断也是必不可少的一部分。
instanceof
ok
false
true
null
undefined
exist
empty
instanceof
和constructor类型判断类似。 用来判断一个对象的原来构造函数是谁。
function changeValue(){}var test1 = new changeValue();
describe('Test',function(){
it('test',()=>{
expect(test1).to.be.instanceof(changeValue);
})
})
ok/true/exist
上述3个语法的关系应该是:exist>ok>true.
由于3者类似,这里就一起说明了。
exist就是用来判断一个值是否存在,即,既不是null也不是undefined.
而,ok是用来判断一个值是否ok(别打我~). 即,该值既不是null,也不是undefined,更不是false
true就很简单,只有true才能pass. 相当于"==="的效果.
详看demo:
//exist->pass
expect("exist").to.exist;
expect(null).to.not.exist;
expect(undefined).to.not.exist;
expect(false).to.exist;
//ok->pass
expect("exist").to.be.ok;
expect(null).to.not.be.ok;
expect(undefined).to.not.be.ok;
expect(false).to.not.be.ok;
//对于上面的exist
//true->pass
expect(true).to.be.true;
expect(1).to.not.be.true;
false/null/undefined/empty
这些都是判断一个内容是否不满足时而造出来语法.
看字面形式可以知道,他们分别对应于自己的类型。
false->false
null->null
undefined->undefined
而,empty则是判断expect的length是否为0. 当然针对于没有length属性的类型。测试用例应该会die。
expect('').to.be.empty;
expect(12).to.not.be.empty;
到这里,expect的断言库,差不多就这些了。 如果大家还有种想要学习的冲动,可以去mocha官网上学习(认真脸)
二、BDD额外语法
上篇介绍了describe的一些基本语法,但是好像还是纰漏了一些,比较高级的API。一个为only.一个为skip.还有一个为done
only
only方法表示在一个block内,只运行该用例的测试。它通常在你满屏输出错误的时候,进行分开修改bug而使用的. 可以在it或者describe上面使用
describe('Test',function(){
it.only('test',()=>{
expect(1).to.equal(1);
});
it("it will output error",function(){
expect(2).to.equal(3);
})
})
//在命令行测试会直接pass。 因为第二个测试用例并没有执行
skip
该方法,用来跳过某个测试用例。 同样在正常情况下,我们不会使用这个API。 它的使用情况依然是,调试你在一个Block里,写出很多个测试时,才会使用.
describe('Test',function(){
it('test',()=>{
expect(1).to.equal(1);
});
it.skip("it will output error",function(){
expect(2).to.equal(3);
})
})
//第一个会提示pass
//第二个会提示pending
done
这个应该是为js量身打造的.因为js本身就是一门异步语言,而mocha无法污染异步的API,所以这个done就是给我们手动去检测异步结束而设立的。来个例子:
//没有异步效果,结果会直接输出I'm testing,而I've finished test却没有效果
describe('Test',function(){
it('test',()=>{
console.log("I'm testing")
setTimeout(function(){
console.log("I've finished test");
},200);
});
})
//这里我们需要加上一个异步的flag--done
describe('Test',function(){
it('test',(done)=>{
//这里加上一个done的参数,表示这是个异步的测试用例
console.log("I'm testing")
setTimeout(function(){
console.log("I've finished test");
done();
//这里手动告诉mocha我已经测试完了
},200);
});
});
//输出的结果为:
//I'm testing
//I've finished test
然而大部分时候,异步测试并不是去测试setTimeout(are u **?). 最主要测试异步的应该是Promise或者是$.Deferred。不过es6最近风行,我们一样以Promise作为例子,Deferred的原理和Promise是一样的.
异步测试Promise
测试Promise的时候,我们当然可以使用上述的done方法来mark一下.
describe('Test',function(){
before(function(){
console.log("I'm testing");
})
it('test',(done)=>{
"use strict";
new Promise(function(resolve,reject){
setTimeout(()=>{
resolve("ok");
}, 200)
})
.then(function(){
console.log("I've finished the test");
done();
})
});
});
//正常情况会输出: 标识成功
// I'm testing
//I've finished the test
但是需要注意的是,如果你的异步执行时间过长,mocha是不会wait u的。 mocha默认的最低时间是2s。所以,如果你的异步时间超出了。像这样:
new Promise(function(resolve,reject){
setTimeout(()=>{
resolve("ok");
}, 2000)
})
.then(function(){
console.log("I've finished the test");
done();
})
他会直接爆出这样的错误:
Error: timeout of 2000ms exceeded
当然,如果你想改变他的默认值设置也可以。想这样设置就over了
mocha -t 10000
或者直接测试用例中单独设置:
describe('Test',function(){
this.timeout(100);
before(function(){
console.log("I'm testing");
});
it('test',(done)=>{
"use strict";
new Promise(function(resolve,reject){
setTimeout(()=>{
resolve("ok");
}, 2000)
})
.then(function(){
console.log("I've finished the test");
done();
})
});
});
//由于你的时间设置过短,所以会出现上述一样的错误
这里的this.timeout只能影响到,当前的测试套件。 在下一个测试套件内,还是依照默认值2000ms。
【第二期读书会送书活动正在进行中】
【您可能感兴趣的文章】
二、Git学习之路
八、浏览器的缓存
前端圈--打造专业的前端技术会议
为web前端开发者提供技术分享和交流的平台
打造一个良好的前端圈生态,推动web标准化的发展
官网:http://fequan.com
微博:fequancom | QQ群:41378087
长按二维码关注我们
投稿:content@fequan.com
赞助合作:apply@fequan.com