基于chai.js官方API文档翻译。仅列出BDD风格的expect/should API。TDD风格的Assert API因为不打算使用,暂时不放,后续可能会更新。javascript
expect
和should
是BDD风格的,两者使用相同的链式语言来组织断言,但不一样在于他们初始化断言的方式:expect使用构造函数来建立断言对象实例,而should经过为Object.prototype新增方法来实现断言(因此should不支持IE);expect
直接指向chai.expect
,而should
则是chai.should()
。css
我的比较建议使用expect
,should不只不兼容IE,在某些状况下还须要改变断言方式来填坑。详细的比较能够看看官网Assertion Styles,说的很清楚。java
var chai = require('chai') ,
expect = chai.expect ,
should = chai.should()
复制代码
下面的接口是单纯做为语言链提供以期提升断言的可读性。除非被插件改写不然它们通常不提供测试功能。node
对以后的断言取反es6
expect(foo).to.not.equal('bar')
expect(goodFn).to.not.throw(Error)
expect({ foo: 'baz'}).to.have.property('foo')
.and.not.equal('bar')
复制代码
设置deep
标记,而后使用equal
和property
断言。该标记可让其后的断言不是比较对象自己,而是递归比较对象的键值对正则表达式
expect(foo).to.deep.equal({ bar: 'baz'})
expect({ foo: { bar: { baz: 'quux'}}})
.to.have.deep.property('foo.bar.baz', 'quux')
复制代码
deep.property
中的特殊符号可使用双反斜杠进行转义(第一个反斜杠是在字符串参数中对第二个反斜杠进行转义,第二个反斜杠用于在property
中进行转义)数组
var deepCss = { '.link': { '[target]': 42 } }
expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42)
复制代码
在keys
断言以前使用any
标记(与all
相反)promise
expect(foo).to.have.any.keys('bar', 'baz')
复制代码
在keys
断言以前使用all
标记(与any
相反)ide
expect(foo).to.have.all.keys('bar', 'baz')
复制代码
a
和an
断言便可做为语言链又可做为断言使用函数
// 类型断言
expect('test').to.be.a('string');
expect({ foo: 'bar' }).to.be.an('object');
expect(null).to.be.a('null');
expect(undefined).to.be.an('undefined');
expect(new Error).to.be.an('error');
expect(new Promise).to.be.a('promise');
expect(new Float32Array()).to.be.a('float32array');
expect(Symbol()).to.be.a('symbol');
// es6 overrides
expect({[Symbol.toStringTag]:()=>'foo'}).to.be.a('foo');
// language chain
expect(foo).to.be.an.instanceof(Foo);
复制代码
include()
和contains()
便可做为属性类断言前缀语言链又可做为做为判断数组、字符串是否包含某值的断言使用。看成为语言链使用时,经常使用于key()
断言以前
expect([1, 2, 3]).to.include(2)
expect('foobar').to.include('bar')
expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo')
复制代码
断言目标为真值。
expect('everything').to.be.ok
expect(1).to.be.ok
expect(false).to.not.be.ok
expect(null).to.not.be.ok
复制代码
断言目标为true
,注意,这里与ok
的区别是不进行类型转换,只能为true
才能经过断言
expect(true).to.be.true
expect(1)to.not.be.true
复制代码
断言目标为false
expect(false).to.be.false
expect(0).to.not.be.false
复制代码
断言目标为null
expect(null).to.be.null
expect(undefined).to.not.be.null
复制代码
断言目标为undefined
。
expect(undefine).to.be.undefined
expect(null).to.not.be.undefined
复制代码
断言目标为非数字NaN
expect('foo').to.be.null
expect(4)to.not.be.null
复制代码
断言目标存在,即非null
也非undefined
var foo = 'hi',
bar = null,
baz
expect(foo).to.exist
expect(bar).to.not.exist
expect(baz).to.not.exist
复制代码
断言目标的长度为0
。对于数组和字符串,它检查length
属性,对于对象,它检查可枚举属性的数量
expect([]).to.be.empty
expect('').to.be.empty
expect({}).to.be.empty
复制代码
断言目标是一个参数对象arguments
function test () {
expect(arguments).to.be.arguments
}
复制代码
断言目标严格等于(===
)value
。另外,若是设置了deep
标记,则断言目标深度等于value
expect('hello').to.equal('hello')
expect(42).to.equal(42)
expect(1).to.not.equal(true)
expect({ foo: 'bar'}).to.not.equal({ foo: 'bar'})
expect({ foo: 'bar'}).to.deep.equal({foo: 'bar'})
复制代码
断言目标深度等于value
,至关于deep.equal(value)
的简写
expect({ foo: 'bar' }).to.eql({ foo: 'bar' })
expect([1, 2, 3]).to.eql([1, 2, 3])
复制代码
断言目标大于(超过)value
expect(10).to.be.above(5)
复制代码
也可接在length
后来断言一个最小的长度。相比直接提供长度的好处是提供了更详细的错误消息
expect('foo').to.have.length.above(2)
expect([1, 2, 3]).to.have.length.above(2)
复制代码
断言目标不小于(大于或等于)value
expect(10).to.be.at.least(10)
复制代码
也可接在length
后来断言一个最小的长度。相比直接提供长度的好处是提供了更详细的错误消息
expect('foo').to.have.length.of.at.least(3)
expect([1, 2, 3]).to.have.length.of.at.least(3)
复制代码
断言目标小于value
expect(5).to.be.below(10)
复制代码
也可接在length后来断言一个最大的长度。相比直接提供长度的好处是提供了更详细的错误消息
expect('foo').to.have.length.below(4)
expect([1, 2, 3]).to.have.length.below(4)
复制代码
断言目标不大于(小于或等于)value
expect(5).to.be.at.most(5)
复制代码
也可接在length后来断言一个最大的长度。相比直接提供长度的好处是提供了更详细的错误消息
expect('foo').to.have.length.of.at.most(4)
expect([1, 2, 3]).to.have.length.of.at.most(3)
复制代码
断言目标在某个区间内
expect(7).to.be.within(5, 10)
复制代码
也可接在length后来断言一个长度区间。相比直接提供长度的好处是提供了更详细的错误消息
expect('foo').to.have.length.within(2, 4)
expect([1, 2, 3]).to.have.length.within(2, 4)
复制代码
断言目标是构造函数constructor
的一个实例
var Tea = function (name) { this.name = name },
Chai = new Tea('chai')
expect(Chai).to.be.an.instanceof(Tea)
expect([1, 2, 3]).to.be.an.instanceof(Array)
复制代码
断言目标是否拥有某个名为name
的属性,可选地若是提供了value
则该属性值还须要严格等于(===
)value
。若是设置了deep
标记,则可使用点.
和中括号[]
来指向对象和数组中的深层属性
// 简单引用
var obj = { foo: 'bar' }
expect(obj).to.have.property('foo')
expect(pbj).to.have.property('foo', 'bar')
// 深层引用
var deepObj = {
green: { tea: 'matcha' },
teas: [ 'Chai', 'matcha', { tea: 'konacha' } ]
}
expect(deepObj).to.have.deep.property('green.tea', 'matcha')
expect(deepObj).to.have.deep.property('teas[1]', 'matcha')
expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha')
复制代码
若是目标是一个数组,还能够直接使用一个或多个数组下标做为name
来在嵌套数组中断言deep.property
var arr = [
[ 'chai', 'matcha', 'konacha' ],
[ { tea: 'chai' },
{ tea: 'matcha' },
{ tea: 'konacha' }
]
]
expect(arr).to.have.deep.property('[0][1]', 'matcha')
expect(arr).to.have.deep.property('[1][2].tea', 'konacha')
复制代码
此外,property
把断言的主语(subject)从原来的对象变为当前属性的值,使得能够在其后进一步衔接其它链式断言(来针对这个属性值进行测试)
expect(obj).to.have.property('foo')
.that.is.a('string')
expect(deepObj).to.have.property('green')
.that.is.an('object')
.that.deep.equals({ tea: 'matcha' })
expect(deepObj).to.have.property('teas')
.that.is.an('array')
.with.deep.property('[2]')
.that.deep.equals({ tea: 'konacha' })
复制代码
注意,只有当设置了deep
标记的时候,在property()
name
中的点(.
)和中括号([]
)才必须使用双反斜杠\
进行转义(为何是双反斜杠,在前文有说起),当没有设置deep
标记的时候,是不能进行转义的
// 简单指向
var css = { '.link[target]': 42 }
expect(css).to.have.property('.link[target]', 42)
//深度指向
var deepCss = { 'link': { '[target]': 42 } }
expect(deepCss).to.have.deep.property('\\.link\\.[target]', 42)
复制代码
name
的自有属性expect('test').to.have.ownProperty('length')
复制代码
断言目标的某个自有属性存在描述符对象,若是给定了descroptor
描述符对象,则该属性的描述符对象必须与其相匹配
expect('test').to.have.ownPropertyDescriptor('length')
expect('test').to.have.ownPropertyDescriptor('length', {
enumerable: false,
configrable: false,
writeable: false,
value: 4
})
expect('test').not.to.have.ownPropertyDescriptor('length', {
enumerable: false,
configurable: false,
writeable: false,
value: 3
})
// 将断言的主语改成了属性描述符对象
expect('test').to.have.ownPropertyDescriptor('length')
.to.have.property('enumerable', false)
expect('test').to.have.ownPropertyDescriptor('length')
.to.have.keys('value')
复制代码
设置.have.length
标记做为比较length
属性值的前缀
expect('foo').to.have.length.above(2)
expect([1, 2, 3]).to.have.length.within(2, 4)
复制代码
断言目标的length
属性为指望的值
expect([1, 2, 3]).to.have.lengthOf(3)
expect('foobar').to.have.lengthOf(6)
复制代码
断言目标匹配到一个正则表达式
expect('foobar').to.match(/^foo/)
复制代码
断言目标字符串包含另外一个字符串
expect('foobar').to.have.string('bar')
复制代码
断言目标包含传入的属性名。与any
,all
,contains
或者have
前缀结合使用会影响测试结果:
当与any
结合使用时,不管是使用have
仍是使用contains
前缀,目标必须至少存在一个传入的属性名才能经过测试。注意,any
或者all
应当至少使用一个,不然默认为all
当结合all
和contains
使用时,目标对象必须至少拥有所有传入的属性名,可是它也能够拥有其它属性名
当结合all
和have
使用时,目标对象必须且仅能拥有所有传入的属性名
// 结合any使用
expect({ foo: 1, bar: 2, baz: 3 }).to.have.any.keys('foo', 'bar')
expect({ foo: 1, bar: 2, baz: 3 }).to.contains.any.keys('foo', 'bar')
// 结合all使用
expect({ foo: 1, bar: 2, baz: 3 }).to.have.all.keys('foo', 'bar', 'baz')
expect({ foo: 1, bar: 2, baz: 3 }).to.contains.all.keys('foo', 'bar')
// 传入string
expect({ foo: 1, bar: 2, baz: 3 }).to.have.any.keys('foo')
// 传入Array
expect({ foo: 1, bar: 2, baz: 3 }).to.have.all.keys(['foo', 'bar', 'baz'])
// 传入Object
expect({ foo: 1, bar: 2, baz: 3 }).to.have.any.keys({ bar: 2, foo: 1 })
复制代码
断言目标函数会抛出一个指定错误或错误类型(使用instanceOf
计算),也可以使用正则表达式或者字符串来检测错误消息
var err = new RefernceError('this is a bad function')
var fn = function () { throw err }
expect(fn).to.throw(ReferenceError)
expect(fn).to.throw(Error)
expect(fn).to.throw(/bad function/)
expect(fn).to.not.throw('good function')
expect(fn).to.throw(ReferrenceError, /bad function/) expect(fn).to.throw(err) 复制代码
注意,当一个抛错断言被否认了(前面有.not
),那么它会从Error构造函数开始依次检查各个可能传入的参数。检查一个只是消息类型不匹配可是已知的错误,合理的方式是先断言该错误存在,而后使用.and
后断言错误消息不匹配
expect(fn).to.throw(ReferenceError)
.and.not.throw(/good function/)
复制代码
####.respondTo(method)
断言目标类或对象会响应一个方法(存在这个方法)
Klass.prototype.bar = function () {}
expect(Klass).to.respondTo('bar')
expect(obj).to.respondTo('bar')
复制代码
若是须要检查一个构造函数是否会响应一个静态方法(挂载在构造函数自己的方法),请查看itself
标记
Klass.baz = function () {}
expect(Klass).itself.to.respondTo('baz')
复制代码
设置itself
标记,而后使用respondTo
断言
function Foo () {}
Foo.bar = function () {}
Foo.prototype.baz = function () {}
expect(Foo).itself.to.respondTo('bar')
expect(Foo).itself.not.to.respond('baz')
复制代码
断言目标值可以让给定的测试器返回真值
expect(1).to.satisfy(function (num) { return num > 0 })
复制代码
断言目标数字等于expected
,或在指望值的+/-delta
范围内
expect(1.5).to.be.closeTo(1, 0.5)
复制代码
断言目标是set
的超集,或前者有后者全部严格相等(===
)的成员。另外,若是设置了deep
标记,则成员进行深度比较(include/contains只能接受单个值,但它们的主语除了是数组,还能够判断字符串;members则将它们的能力扩展为可以接受一个数组,但主语只能是数组)
expect([1, 2, 3]).to.include.members([3, 2])
expect([1, 2, 3]).to.not.include.members([3, 2, 8])
expect([4, 2]).to.have.members([2, 4])
expect([5, 2]).to.not.have.members([5, 2, 1])
expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }])
复制代码
断言目标值出如今list
数组的某个顶层位置(直接子元素,严格相等)
expect('a').to.be.oneOf(['a', 'b', 'c'])
expect(9).to.not.be.oneOf(['z'])
// 严格相等,因此对象类的值必须为同一个引用才能被断定为相等
var three = [3]
expect([3]).to.not.be.oneOf([1, 2, [3]])
expect(three).to.not.be.oneOf([1, 2, [3]])
expect(three).to.be.oneOf([1, 2, three])
复制代码
断言目标方法会改变指定对象的指定属性
var obj = { val: 10 }
var fn = function () { obj.val += 3 }
var noChangeFn = function () { return 'bar' + 'baz' }
expect(fn).to.change(obj, 'val')
复制代码
断言目标方法会增长指定对象的属性
var obj = { val: 10 }
var fn = function () { obj.val = 15 }
expect(fn).to.increase(obj, val)
复制代码
断言目标方法会减小指定对象的属性
var obj = { val: 10 }
var fn = function () { obj.val = 5 }
expect(fn).to.decrease(obj, val)
复制代码
断言目标对象是可扩展的(能够添加新的属性)
var nonExtensibleObject = Object.preventExtensions({})
var sealedObject = Object.seal({})
var frozenObject = Object.freeze({})
expect({}).to.be.extensible
expect(nonExtensibleObject).to.not.be.extensible
expect(sealObject).to.not.be.extensible
expect(frozenObject).to.not.be.extensible
复制代码
断言目标对象是封闭的(没法添加新的属性而且存在的属性不能被删除但能够被修改)
var sealedObject= Object.seal({})
var frozenObject = Object.freeze({})
expect(sealedObject).to.be.sealed
expect(frozenObject).to.be.sealed
expect({}).to.not.be.sealed
复制代码
断言目标对象是冻结的(没法添加新的属性而且存在的属性不能被删除和修改)
var frozenObject = Object.freeze({})
expect(frozenObject).to.be.frozen
expect({}).to.not.be.frozen
复制代码
除了一些语法糖之外,Chai提供的assert
风格的断言和node.js包含的assert模块很是类似。assert
风格是三种断言风格中惟一不支持链式调用的。 // TODO API 参考