Chai介绍
Chai 是一个针对 Node.js 和浏览器的行为驱动测试和测试驱动测试的诊断库,可与任何 JavaScript 测试框架集成。
本文介绍的Chai下的expect/should风格属于BDD(行为驱动开发)
因为本身语法相对简单,所以本文是类似于api的中文文档,其中有许多不足,见谅。
正片开始
to
be
been
is
that
which
and
has
have
with
at
of
same
以上仅做语义化用,无实际作用
具体api介绍
.not
对之后的断言取反
1 2 3 4 expect({a : 1 }).to.not.equal({ a : 1 }) expect({ a : 1 }).to.be.not.have.a.keys('b' ) expect([100 , 200 ]).to.be.not.lengthOf(3 ) expect([1 , 2 ]).to.be.length.not.within(-1 , 1 )
顶
.deep
普通的对象或数组比较是比较它们的整体是否相等。
比如
1 2 3 4 5 6 expect({ a : 1 }).to.be.not.equal({ a : 1 }) expect([1 , 2 ]).to.be.not.equal([1 , 2 ]) expect({ a : 1 }).to.be.deep.equal({ a : 1 }) expect([1 , 2 ]).to.be.deep.equal([1 , 2 ]) expect({ a : { b : { c : 3 } } }).to.be.deep.equal({ a : { b : { c : 3 } } }) expect({ a : { b : 2 } }).to.have.nested.deep.property('a.b' )
相当于是严格相等 ===
顶
.any
与keys
使用时至少满足一项
1 2 expect([100 , 200 ]).to.have.any.keys(0 ) expect({ a : 100 , b : 200 }).to.have.any.keys('a' , 'b' )
顶
.all
与any
类似,但是至少满足所有项
1 2 3 4 expect([100 , 200 ]).to.has.all.keys(0 , 1 ) expect([100 , 200 ]).to.not.has.all.keys(0 ) expect({ a : 100 , b : 200 }).to.has.all.keys('a' , 'b' ) expect({ a : 1 , b : 2 , c : 3 }).to.not.has.all.keys('a' , 'b' )
需要注意的是,如上最后一条断言所示,当all
和keys
一同使用时,断言对象的key
必须和keys
完全一样才能通过
顶
.a .an
既可以用做链式判断也可用于断言
用作判断时无实际作用,仅用于语义化
用做断言时用于判断值是否为某一类型
方法type a = (type: 'string' | 'object' | 'null' | 'undefined' | 'array' | 'number' | 'symbol' | 'error' | 'promise'/*还有别的类型...*/, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 13 expect(100 ).to.be.a('number' ) expect({ a : 100 }).to.has.a.property('a' ) expect(Symbol ()).to.be.a('symbol' ) expect(new Error ()).to.be.a('error' )const object = { [Symbol .toStringTag]: 'diyObj' } expect(object).to.be.a('diyObj' ) expect(100 ).to.be.a('number' ).and.to.equal(100 ) expect(100 ).to.be.a('number' , 'it is not impossible' )
可用于自定义类型进行判断,如上使用Symbol.toStringTag
进行自定义类型定义
由a
和an
断言会返回断言的值,可以继续做链式调用继续做其他判断
a
和an
有可选的第二参数可以指定出错时的错误信息
顶
.include .contains
用于判断断言值是否包含某个指定的值,但是只能指定一个值
includes
、 contains
contain
同义
既可以用做链式判断也可用于断言
方法 type include = (value: any, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 expect('Daniel' ).to.be.include('iel' ) expect([1 , 2 ]).to.be.a('array' ).and.include(1 ) expect([1 , '2' ]).to.not.be.include(2 ) expect({ a : 1 , b : 2 }).to.be.include({ a : 1 }) expect(new Map ([[1 , 2 ], [3 , 4 ]])).to.be.include(2 ) expect(new Set ([1 , 2 , 3 ])).to.be.include(3 ) expect({ a : { b : 2 } }).to.be.not.include({ a : { b : 2 } }) expect({ a : { b : 2 } }).to.be.deep.include({ a : { b : 2 } }) expect({ a : { b : 2 } }).to.be.nested.deep.include({ 'a.b' : 2 }) expect([1 , 2 ]).to.be.not.include(3 , 'it is not impossible' ) expect([1 , 2 , 3 ]).to.be.include.members([1 , 2 ]) expect({ a : 1 , b : 2 , c : 3 }).to.be.include.any.keys('a' , 'b' ) expect({ a : 1 , b : 2 }).to.be.include.all.keys('a' , 'b' )Object .prototype.say = 'Daniel' expect({}).to.be.include({ say : 'Daniel' }).but.not.own.include({ say : 'Daniel' })
顶
.nested
用于在property
、include
等用.
语法获取深层次属性
有时候对于深层次的对象或数组无法直接判断该内容,可以通过nested
来实现深层次的值获取
1 2 3 4 expect({ a : { b : { c : 1 } } }).to.be.nested.have.a.property('a.b.c' ) expect({ '.a' : { '..b' : { '[c]' : 1 } } }).to.be.nested.have.a.property('\\.a.\\.\\.b.\\[c\\]' )
顶
.ok
表示真值,如true
[]
{}
等
1 2 3 4 expect([]).to.be.ok expect({}).to.be.ok expect(false , 'it is not impossible' ).to.be.not.ok
一般情况下不太需要使用此属性
关于其他象征性的值直接使用对应的属性就可以
1 2 3 4 expect(0 ).to.be.equal(0 ) expect(true ).to.be.true expect(null ).to.be.null expect(undefined ).to.be.undefined
顶
.true
同上表示真值,但不进行类型转换,所以只有true
能通过
顶
false
与true
相反,表示假值,不进行类型转换
1 expect(false ).to.be.false
顶
.null
用于判断值是否为null
值
1 expect(false ).to.be.false
顶
.undefined
用于判断值是否为undefined
值
1 expect(undefined ).to.be.undefined
顶
.NaN
用于判断值是否为NaN
值
1 2 3 expect(100 ).to.be.not.NaN
顶
.exist
用于判断值是否存在,即非null
且 非undefined
1 2 3 4 expect(100 ).to.be.exist expect(null ).to.be.not.exist expect([]).to.be.exist expect(undefined ).to.be.not.exist
顶
.finite
表示有限数组,即非NaN
且 非Infinity
1 2 3 expect(200 ).to.be.finite expect(Infinity ).to.be.not.finite expect('Daniel' ).to.be.not.finite
顶
.empty
判断值得长度是否为0
1 2 3 4 5 6 expect('' ).to.be.empty expect([]).to.be.empty expect({}).to.be.empty
顶
.arguments
判断值是否为arguments
对象
1 expect(arguments ).to.be.arguments
顶
.equal
判断值是否严格相等,相当于===
type equal = (value: any, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 expect(1 ).to.be.equal(1 ) expect([1 , 2 , 3 ]).to.be.not.equal([1 , 2 , 3 ]) expect([1 , 2 , 3 ]).to.be.deep.equal([1 , 2 , 3 ]) expect({ a : 1 }).to.be.deep.equal({ a : 1 }) expect([1 , 2 ]).to.be.not.equal([1 , 2 ], 'it is not impossible' )
顶
.eql
可以当做是deep
和 equal
的组合
type eql = (value: any, errmsg?: string) => any
1 2 3 4 5 expect([1 , 2 , 3 ]).to.be.eql([1 , 2 , 3 ]) expect({ a : 1 }).to.be.eql({ a : 1 }) expect({ a : 1 }).to.be.eql({ a : 1 }).and.not.be.empty
如上所例🌰,eql返回值本身,可以继续向后做断言
顶
.above
判断值是否大于
指定值
type above = (value: number, errmsg?: string) => any
1 2 expect(100 ).to.be.above(10 ) expect(100 ).to.be.not.above(101 )
对于字符串,数组等长度的比较建议直接使用lengthOf
顶
.least
判断值是否不小于(大于等于)
指定值
type least = (value: number, errmsg?: string) => any
1 2 expect(100 ).to.be.least(100 ) expect(100 ).to.be.least(99 )
顶
.below
判断值是否小于
指定值
type below = (value: number, errmsg?: string) => any
1 2 expect(100 ).to.be.below(101 ) expect(100 ).to.be.not.below(99 )
顶
.most
判断值是否不大于(小于等于)
指定值
type most = (value: number, errmsg?: string) => any
1 2 expect(100 ).to.be.most(100 ) expect(100 ).to.be.most(101 )
顶
.within
判断值是否在指定区间内
type within = (start: number, end: number, errmsg?: string) => any
1 2 3 4 expect(100 ).to.be.within(0 , 100 ) expect('Daniel' ).to.have.a.lengthOf.within(0 , 100 ) expect([1 , 2 , 3 ]).to.have.a.lengthOf.within(0 , 100 )
不推荐使用该方法,因为它能够实现的测试基本都可以通过equal
或是lengthOf
来实现
1 2 3 4 expect(100 ).to.be.equal(100 ) expect('Daniel' ).to.have.a.lengthOf(6 ) expect([1 , 2 , 3 ]).to.have.a.lengthOf(3 )
顶
.instanceof
判断值是否为指定值的实例
type instanceof = (constructor: object, errmsg?: string) => any
1 2 3 4 5 6 function Father ( ) {}const father = new Father() expect(father).to.be.instanceof(Father) expect([]).to.be.instanceof(Array ) expect(new Number (100 )).to.be.instanceof(Number ) expect(new String ('Daniel' )).to.be.instanceof(String )
顶
.property
判断值是否包含指定属性
type property = (key: string, value?: any, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 expect({ a : 1 }).to.be.have.a.property('a' ) expect({ a : 1 }).to.be.have.a.property('a' , 1 ) expect({ a : { b : 1 } }).to.be.not.have.a.property('a' , { b : 1 }) expect({ a : { b : 1 } }).to.be.have.a.deep.property('a' , { b : 1 }) expect({ a : 1 }).to.be.have.a.property('a' ) expect({ a : 1 }).to.be.have.a.property('toString' ) expect({ a : 1 }).to.be.have.a.own.property('a' ) expect({ a : 1 }).to.be.not.have.a.own.property('toString' ) expect({ a : { b : 2 } }).to.be.have.nested.deep.property('a.b' , 2 )
顶
.own
判断值是否拥有指定的属性,不包含上层属性
1 2 3 4 5 expect({ a : 1 }).to.be.have.own.property('a' ) expect({ a : 1 }).to.be.not.have.own.property('toString' ) expect({ a : { b : 2 } }).to.be.have.own.deep.property('a' , { b : 2 })
顶
.ownProperty
判断是否为本身属性,相当于是 .own.property
的结合
type ownProperty = (key: string, value?: any, errmsg?: string) => any
1 2 expect({ a : 1 }).to.be.have.own.property('a' ) expect({ a : 1 }).to.be.have.ownProperty('a' )
顶
.ownPropertyDescriptor
类似于上面的方法,用于判断是否为本身的属性
但是它可以传递第二参数,表示该属性的描述对象,不知道什么是描述对象的自行百度
type ownPropertyDescriptor = (key: string, value?: { get?: (undifiend || () => any)=undefined, set?: (undefined || () => any)=undefined, configurable?: boolean=false, enumerable?: boolean=false, value?: any=undefined, writable?: boolean=false }, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const object = { name : 'Daniel' }const descriper = { enumerable : true , configurable : true , get ( ) { return 'I love China' }, set ( ) { return this .value } }Object .defineProperty(object, 'name' , descriper) expect(object).to.be.ownPropertyDescriptor('name' ) expect(object).to.be.ownPropertyDescriptor('name' , descriper) expect(object).to.be.ownPropertyDescriptor('name' , descriper, 'it is impossible' )
顶
.lengthOf .length
判断值是否存在length
属性且为指定值
方法 type lengthOf = (value: number, errmsg?: string) => any
1 2 3 expect([1 , 2 , 3 ]).to.be.lengthOf(3 ) expect([1 , 2 , 3 ]).to.have.a.lengthOf.below(1 )
顶
.match
判断值是否与指定正则
匹配
和普通match
方法一样,所以主语就是string
matches
效果与match
相同
type match = (reg: Regexp, errmsg?: string) => any
1 2 3 expect('Daniel' ).to.be.match(/^D.+l$/ ) expect('2020-08-10' ).to.be.match(/^\d{4}-\d{2}-\d{2}$/ ) expect('13456787654' ).to.be.match(/^1[^12]\d{9}$/ )
顶
.string
判断值是否包含指定的字符串
type string = (value: string, errmsg?: string) => any
1 2 3 4 expect('Daniel' ).to.be.have.a.string('iel' ) expect('hello' ).to.be.not.have.a.string('world' ) expect('world' ).to.be.not.have.a.string('rd' )
顶
.keys
用于判断数组
、对象
、Set
、Map
等是否包含相应的key
type keys = (...args: Array<string | number>) => any
1 2 3 4 5 6 7 8 9 10 11 12 expect({ a : 1 }).to.be.have.keys('a' ) expect([1 , 2 ]).to.be.have.keys('0' , '1' ) expect(new Set ([1 , 2 ])).to.be.have.keys(1 , 2 ) expect(new Map ([[ 'a' , 1 ]])).to.be.have.keys('a' ) expect({ a : 1 , b : 2 }).to.be.have.any.keys('a' ) expect({ a : 1 , b : 2 }).to.be.be.have.all.keys('a' , 'b' ) expect({ a : 1 , b : 2 , c : 3 }).to.be.include.keys('a' , 'b' ) expect({ a : { b : { c : 3 } } }).to.be.have.deep.keys('a.b' )
顶
.throw
判断值是否抛出指定的错误、错误信息等。
.throw(errConstructor | string | reg) 是否抛出指定错误或字符串或匹配指定错误信息
type throw = (errorLike?: Error | Construcotr, errMsgMatcher?: string | Regexp, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const errors = new TypeError ('oops' )function error ( ) { throw errors } expect(error).to.be.throw() expect(error).to.be.throw(TypeError ) expect(error).to.be.throw(errors, 'oops' ) expect(error).to.be.throw(errors, 'oop' ) expect(error).to.be.throw(TypeError , /.+/ ) expect(error).to.be.throw('oop' ) expect(error).to.be.throw(/^o.+s$/ ) errors.code = 404 expect(error).to.be.throw(errors).and.that.have.a.property('code' , 404 )
throws
和 Throw
同义🙆
顶
.respondTo
用于判断一个对象或构造函数是否有相关的方法
type respondTo = (methodName: string ,errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 function Father ( ) {} Father.prototype.sayFather = function ( ) {} Father.saySon = function ( ) {} expect(new Father()).to.be.respondTo('sayFather' ) expect(new Father()).to.be.not.respondTo('saySon' ) expect({}).to.be.respondTo('toString' ) expect(Father).to.be.itself.respondTo('saySon' ).but.not.respondTo('sayFather' )
respondsTo
与此方法同义
顶
.itself
配合上述的respondTo
方法判断值是否响应指定的方法,但是为自身的方法,不包含prototype
上的方法
具体事例可以看上面的respondTo
顶
.satisfy
接收一个函数作为参数,函数参数为断言目标值,返回值为boolean,判断是否通过断言
type satisfy = (method: (target: any) => boolean, errmsg?: string) => any
1 2 3 4 5 6 7 expect(100 ).to.be.satisfy(function (target ) { return target >= 0 && target <= 100 }) expect(100 ).to.be.satisfies(function (target ) { return target >= 0 && target <= 100 })
satisfies
与此方法同义
顶
.closeTo
指定值(仅数字
)是否在期望值的接收范围内
此方法与within
的区别就是within接收的两个参数是上限和下限
closeTo
的参数,第一参数为期望的中间值,第二参数为上下限(第一参数加减第二参数)
type closeTo = (expected: number, delta: number, errmsg?: string) => any
1 2 3 4 5 expect(100 ).to.be.closeTo(10 , 100 ) expect(110 ).to.be.closeTo(10 , 100 ) expect(-90 ).to.be.closeTo(10 , 100 )
虽然此方法可以用于判断值是否在一个区间内,但是直接使用equal
进行相等判断一样可以
顶
.members
类似于contains、include,但是接收参数为数组,主语只能是数组
type members = (target: Array<any>, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 expect([1 , 2 , 3 ]).to.be.have.members([1 , 2 , 3 ]) expect([ { a : 1 } ]).to.be.have.deep.members([ { a : 1 } ]) expect([ 1 , 2 , 3 ]).to.be.have.ordered.members([ 1 , 2 , 3 ]) expect([1 , 2 , 3 ]).to.be.include.members([ 1 , 2 ]) expect([1 ]).to.be.not.include(2 ).and.not.include(3 ) expect([ { a : 1 }, { b : 2 }, { c : 3 } ]).to.be.include.deep.ordered.members([ { a : 1 }, { b : 2 } ])
顶
.oneOf
判断值是否出现在指定数组中
type oneOf = (list: Array<any>, errmsg?: string) => any
1 2 3 4 expect(100 ).to.be.oneOf([1 , 2 , 100 ]) expect(100 ).to.be.equal(100 )
顶
.change
判断方法是否会改变指定的值,主语接收的是一个改变判定对象的值的方法
可以把它当做是increase
和decrease
的结合
但是推荐直接使用上述两个方法
type change = (value: ((value: any) => any) | any, prop?: string, errmsg?: string) => any
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 let number = 0 let string = '0' let array = [ 0 ]let object = { 0 : 0 }function changeNumber ( ) { number ++ }function changeString ( ) { string += 1 }function changeArray ( ) { array[0 ] = 1 }function changeObject ( ) { object[0 ] += 1 }function getNumber ( ) { return number }function getString ( ) { return string } expect(changeNumber).to.be.change(getNumber) expect(changeString).to.be.change(getString) expect(changeArray).to.be.change(array, '0' ) expect(changeObject).to.be.change(object, '0' ) expect(changeNumber).to.be.change(getNumber).by(1 )
顶
.increase | .decrease
increase
判断方法是否会增加指定对象的属性或者普通值的值
decrease
判断方法是否会减少指定对象的属性或者普通值的值
const increase = (value: any, prop?: string, errmsg?: string)
const decrease = (value: any, prop?: string, errmsg?: string)
并且只能是数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let number = 0 function increase ( ) { number ++ }function decrease ( ) { number -- }function get ( ) { return number } expect(increase).to.be.increase(get) expect(decrease).to.be.decrease(get)let obj = { a : 1 }function objIncrease ( ) { obj.a ++ }function objDecrease ( ) { obj.a -- } expect(objIncrease).to.be.increase(obj, 'a' ) expect(objDecrease).to.be.decrease(obj, 'a' )let arr = [1 , 2 , 3 ]function arrayIncrease ( ) { arr[0 ] += 2 }function arrayDecrease ( ) { arr[0 ] -= 2 } expect(arrayIncrease).to.be.increase(arr, '0' ).by(2 ) expect(arrayDecrease).to.be.decrease(arr, '0' ).by(2 ) expect(objIncrease).to.be.increase(obj, 'a' ).by(1 ) expect(objDecrease).to.be.decrease(obj, 'a' ).by(1 )
顶
.extensible
判断指定值是否可扩展(不可以添加新属性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let object = { name : 'Daniel' , sayName : function ( ) { return this .name } }Object .preventExtensions(object)console .log(object) object.name = 'Mike' console .log(object)console .log(object)delete object.nameconsole .log(object)console .log(object) object.age = 18 console .log(object) expect(object).to.be.extensible
顶
.sealed
判断指定值是否封闭(不可添加新属性、不可删除旧属性、可修改旧属性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 let object = { name : 'Daniel' , sayName : function ( ) { return this .name } }Object .seal(object)console .log(object) object.age = 18 console .log(object)console .log(object) object.name = 'Mike' console .log(object)console .log(object)delete object.nameconsole .log(object) expect(object).to.be.sealed
顶
.frozen
判断值是否冻结(不可添加新属性、不可删除旧属性、不可修改旧属性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 let object = { name : 'Daniel' , sayName : function ( ) { return this .name } }Object .freeze(object)console .log(object) object.age = 18 console .log(object)console .log(object) object.name = 'Mike' console .log(object)console .log(object)delete object.nameconsole .log(object) expect(object).to.be.frozen
顶
.by
配合descrease
或 increase
、 change
等来判断断言值是否发生变化在指定范围内
具体事例🌰可以直接查看上述三个方法的例子👆
顶
.fail
判断是否会失败
有两种参数传递方式
type fail = (message?: string) => any
type fail = (actual: any, expected: any, message?: string, operator: string) => any
1 2 3 4 5 6 7 expect.fail() expect.fail('it is error' ) expect.fail(1 , 2 , 'it is impossible' ) expect.fail(1 , 2 , 'it is impossible' , '>' )
顶
.ordered
配合members
用于指定members
参数数组的顺序与主语数组的顺序一致
1 2 3 expect([1 , 2 , 3 ]).to.be.ordered.members([1 , 2 , 3 ]) expect([1 , 2 , 3 ]).to.be.not.ordered.members([2 , 1 , 3 ])
顶
完结
😊
如果有什么不对的地方,请指出。