原文:http://cnpmjs.org/package/should
assert是用于编写单元测试的断言库。should其实和assert的作用一样,它包装了assert,改变了书写风格。
should库扩展了Object.prototype,额外提供了".should"语法。一旦引入了should,即“var should = require('should');”,那么当前js文件内的所有继承至Object的对象都可以.should。如下所示例:
var should = require('should'); //引入了你就可以用Object.should了
//被测试的目标对象
var user = {
name: 'tj'
, pets: ['tobi', 'loki', 'jane', 'bandit']};
user.should.have.property('name', 'tj');
user.should.have.property('pets').with.lengthOf(4);
// 如果目标对象没有继承至Object,那就换一种方式使用:should(user).have.property('name', 'tj');
// null就没有继承至Objectshould(null).not.be.ok;
someAsyncTask(foo, function(err, result){
should.not.exist(err);
should.exist(result);
result.bar.should.equal(foo);});
安装
should库需要额外安装:$ npm install should --save-dev
浏览器端将should包下的should.js引入到前台页面,即可和后端一样使用。
Static should and assert module
should的静态方法
should提供了一系列的静态方法,这些方法可以对非Object对象使用。它们和assert的基本方法其实是一样的。
可以只熟悉should的方法。这里不用看,晃一眼即可
assert.fail(actual, expected, message, operator) // just write wrong should assertion
assert(value, message), assert.ok(value, [message]) // should(value).okassert.equal(actual, expected, [message]) // should(actual).eql(expected, [message])assert.notEqual(actual, expected, [message]) // should(actual).not.eql(expected, [message])
assert.deepEqual(actual, expected, [message]) // should(actual).eql(expected, [message])
assert.notDeepEqual(actual, expected, [message]) // should(actual).not.eql(expected, [message])
assert.strictEqual(actual, expected, [message]) // should(actual).equal(expected, [message])
assert.notStrictEqual(actual, expected, [message]) // should(actual).not.equal(expected, [message])
assert.throws(block, [error], [message]) // should(block).throw([error])assert.doesNotThrow(block, [message]) // should(block).not.throw([error])
assert.ifError(value) // should(value).Error (to check if it is error) or should(value).not.ok (to check that it is falsy)
.not
.not
对当前的断言取反。
.any
.any和array.some方法类似,他可以接受多个参数或者数组,只要其中一个满足断言,即可。
.any和.not一起使用则会出错。
Assertions 断言
每一个should断言都返回一个被should.js包装过的对象,所以他可以形成链式语法,但是有一些断言,比如.length,.property等将被断言的对象改变成了其属性值。
。而且在“链”中,可以使用下面等方便理解的词,这些词其实对should的断言一点实际影响都没有,注意,没有.or。
.an
, .of
, .a
, .and
, .be
, .have
, .with
, .is
, .which
.
user.should.be.an.instanceOf(Object).and.have.property('name', 'tj');
//其实就是user.should.instanceOf(Object).property('name', 'tj');
user.pets.should.be.instanceof(Array).and.have.lengthOf(4);
//其实就是user.pets.should.instanceof(Array).lenthOf(4);
.ok
断言当前的对象为真(==true),注意不是恒等,('', null, undefined, 0 , NaN 等都不ok)
注意不能对null,underfined使用ok,比如: undefined.should.not.be.ok;
为了测试是否为null,可以这样:(err === null).should.be.true;
.true
断言当前对象是否恒等true
.false
断言当前对象是否恒等true
false.should.be.false;
(0).should.not.be.false;
.eql(otherValue)
断言当前对象是否等于某个值,这过程会隐式转换类型,而且即使只有值相等即可。
[1,2,3].should.eql([1,2,3]);
// 下面的例子隐式转换了类型
[1, 2, 3].should.eql({ '0': 1, '1': 2, '2': 3 });
.equal(otherValue) and .exactly(otherValue)
断言当前对象是否与另一对象相同,===,恒等,用exactly比较好理解
(4).should.equal(4);
'test'.should.equal('test');
[1,2,3].should.not.equal([1,2,3]);
(4).should.be.exactly(4);
.startWith(str)
断言一个字符串的开始部分是另一个字符串
'foobar'.should.startWith('foo');
'foobar'.should.not.startWith('bar');
.endWith(str)
//断言一个字符串的结束部分是另一个字符串
'foobar'.should.endWith('bar');
'foobar'.should.not.endWith('foo');
.within(from, to)
断言一个数字,在某一个范围之内(
<= to and >= from
):
user.age.should.be.within(5, 50);
(5).should.be.within(5, 10).and.within(5, 5);
.approximately(num, delta)
断言某个浮点型数字在X+(-)Y的范围内.
(99.99).should.be.approximately(100, 0.1);
.above(num) and .greaterThan(num)
断言一个数字比某数字大,(>num)
user.age.should.be.above(5);
user.age.should.not.be.above(100);
(5).should.be.above(0);
(5).should.not.be.above(5);
.below(num) and .lessThan(num)
断言一个数字比某数字小(<num)
user.age.should.not.be.below(5);
(5).should.be.below(6);
(5).should.not.be.below(5);
.NaN
//断言某值为nan
(undefined + 0).should.be.NaN;
.Infinity
//断言某值为无穷大
(1/0).should.be.Infinity;
.type(str)
//判断内置类型
user.should.be.type('object');'test'.should.be.type('string');
//附:这个断言一般可以不用,而是用Object.prototype.toString.call(obj).slice(8, -1)判断其内置class,
JavaScript 类型表格(The JavaScript type table)
Value Class Type
-------------------------------------
"foo" String string
new String("foo") String object
1.2 Number number
new Number(1.2) Number object
true Boolean boolean
new Boolean(true) Boolean object
new Date() Date object
new Error() Error object
[1,2,3] Array object
new Array(1, 2, 3) Array object
new Function("") Function function
/abc/g RegExp object (function in Nitro/V8)
new RegExp("meow") RegExp object (function in Nitro/V8)
{} Object object
new Object() Object object
null Null object
undefined Undefined undefined
NaN Number number
//对于内置类型,使用typeof一般没啥用。判断出来几乎都是Object有毛用。
typeof可以用来typeof xx === undefined判断xx是否为undefined,即xx是否已经被定义;其实xx === undefined也可以。。。
所以可以使用Object.prototype.toString.call(xxx).slice(8, -1)==='typestring'
is('Array',[1, 2]).should.be.ok;//这样判断就ok了
.instanceof(constructor) and .instanceOf(constructor)
断言用户自定义类型(使用instanceof)
user.should.be.an.instanceof(User);
[].should.be.an.instanceOf(Array);
.Object, .Number, .Array, .Boolean, .Function, .String, .Error
//判断内置类型,这些方法比上面的type又更实用一点。还有.NaN,
({}).should.be.an.Object;
(1).should.be.a.Number;
[].should.be.an.Array.and.an.Object;
(true).should.be.a.Boolean;
''.should.be.a.String;
.enumerable(name[, value])
断言是否拥有一个可枚举属性name。value表示其属性的值(使用.eql比较,即==)。
should({name:'lee',age:19}).enumerable('name');
should({name:'lee',age:19}).not.enumerable('toString');
.property(name[, value])
断言是否拥有一个属性,包括继承属性
should({name:'lee',age:19}).property('name');
should({name:'lee',age:19}).property('toString');
注意:.property断言会改变链式结构中的对象,改变为被断言属性的值。这个要小心。
.properties(propName1, propName2, ...) or .properties([propName1, propName2, ...]) or .properties(obj)
断言拥有一系列属性,还可以判断值。
user.should.have.properties('name', 'age');
user.should.have.properties(['name', 'age']);
user.should.have.properties({
name: 'denis',
age: 24});
.length(number) and .lengthOf(number)
断言具有length属性,并且等于某值。
user.pets.should.have.length(5);
user.pets.should.have.a.lengthOf(5);
({ length: 10}).should.have.length(10);
//注意length lengthOf也会改变链式结构的对象
.empty
断言为空,比如String,arrays,argunments的length为0,或者对象没有自己的属性。这个有用。
[].should.be.empty;''.should.be.empty;({}).should.be.empty;(function() {
arguments.should.be.empty;})();
.containEql(otherValue)
断言‘包含’某组成部分,字符串类似contain,数组、对象类似in。
'hello boy'.should.containEql('boy');
[1,2,3].should.containEql(3);
[[1],[2],[3]].should.containEql([3]);
[[1],[2],[3, 4]].should.not.containEql([3]);
({ b: 10 }).should.containEql({ b: 10 });
([1, 2, { a: 10 }]).should.containEql({ a: 10 });
[1, 2, 3].should.not.containEql({ a: 1 });
[{a: 'a'}, {b: 'b', c: 'c'}].should.containEql({a: 'a'});
[{a: 'a'}, {b: 'b', c: 'c'}].should.not.containEql({b: 'b'});
.match(otherValue)
.match非常强大,断言matchs某正则,某自定义函数
被断言是个字符串:判断匹配给定的正则。
username.should.match(/^\w+$/)
被断言是个数组:判断每个值都匹配给定的正则。
['a', 'b', 'c'].should.not.match(/[d-z]/);
被断言是个对象:判断每个自有属性的值都匹配给定的正则。
({ a: 'foo', c: 'barfoo' }).should.match(/foo$/);
({ a: 'a' }).should.not.match(/^http/);
还可以判断给定的值匹配自定义函数的逻辑。函数内可以在判断逻辑后,返回false、true。也只能返回真假。
(5).should.match(function(n) { return n > 0; });
(5).should.not.match(function(n) { return n < 0; });
(5).should.not.match(function(it) { it.should.be.an.Array; });
(5).should.match(function(it) { return it.should.be.a.Number; });
如果给定的是一个对象,可以分别对其的属性做断言,有点像containDeep,如下:
({ a: 10, b: 'abc', c: { d: 10 }, d: 0 }).should.match({ a: 10, b: /c$/, c: function(it) { return it.should.have.property('d', 10); }});
[10, 'abc', { d: 10 }, 0].should.match({ '0': 10, '1': /c$/, '2': function(it) { return it.should.have.property('d', 10); } });
[10, 'abc', { d: 10 }, 0].should.match([10, /c$/, function(it) { return it.should.have.property('d', 10); }]);
.matchEach(otherValue)
matchEach和match对比,如果被断言的是个值类型,两个方法都一样;
如果是个数组、对象之类的,则match可以对其中每个属性分别做断言,
而matchEach则是对每个属性做一个统一的判断(可以是函数)
另外,对数组、对象这些用match,不能用function对所有属性统一作判断。下面的就出错
[10, 11, 12].should.match(function(it) { return it >= 10; });
所以,一般用matchEach即可,如果要对对象的每个属性分别判断,用containEql,property等即可。
(['a', 'b', 'c']).should.matchEach(/[a-c]/);
[10, 11, 12].should.matchEach(function(it) { return it >= 10; });
[10, 10].should.matchEach(10);
.throw() and throwError()
//断言抛出了错误
(function(){throw new Error('fail');}).should.throw();
//断言没有抛出错误
(function(){}).should.not.throw();
//断言抛出错误的文字内容。这个是根据Error的结构来的。
(function(){throw new Error('fail');}).should.throw('fail');
//断言抛出的错误匹配正则
(function(){throw new Error('failed to foo');}).should.throw(/^fail/);
Error properties to match some other properties (used ):
var error = new Error();
error.a = 10;(function(){ throw error; }).should.throw({ a: 10 });
(function(){ throw error; }).should.throw(Error, { a: 10 });
f
unction isPositive(n) {if(n <= 0) throw new Error('Given number is not positive')}
isPositive.bind(null, 10).should.not.throw();
isPositive.bind(null, -10).should.throw();
要测试异步函数,需要测试该函数是否被执行,还需要在该函数内部测试需要测试的项。结合mocha来比较方便
// first we need to check that function is calledvar called = false;
collection.findOne({ _id: 10 }, function(err, res) {
called = true;
//second we test what you want
res.should.be....});
called.should.be.true;
//结合mocha:
collection.findOne({ _id: 10 }, function(err, res) {
if(err) return done(err);
//second we test what you want
res.should.be.... done();});
Optional Error description可选的错误描述参数
(1).should.eql(0, 'some useful description')
结果:
AssertionError: some useful description
at Object.eql (/Users/swift/code/should.js/node_modules/should/lib/should.js:280:10)
...
如下的断言均具有此参数: eql, equal, within, instanceof, above, below, match, length, property, ownProperty.