基于chai.js官方API文档翻译。仅列出BDD风格的expect/should API。TDD风格的Assert API由于不打算使用,暂时不放,后续可能会更新。

BDD

expectshould是BDD风格的,二者使用相同的链式语言来组织断言,但不同在于他们初始化断言的方式:expect使用构造函数来创建断言对象实例,而should通过为Object.prototype新增方法来实现断言(所以should不支持IE);expect直接指向chai.expect,而should则是chai.should()
个人比较建议使用expect,should不仅不兼容IE,在某些情况下还需要改变断言方式来填坑。详细的比较可以看看官网Assertion Styles,说的很清楚。

  1. var chai = require('chai') ,
  2. expect = chai.expect ,
  3. should = chai.should()

语言链

下面的接口是单纯作为语言链提供以期提高断言的可读性。除非被插件改写否则它们一般不提供测试功能。

  • to
  • be
  • been
  • is
  • that
  • which
  • and
  • has
  • have
  • with
  • at
  • of
  • same

    .not

    对之后的断言取反

    1. expect(foo).to.not.equal('bar')
    2. expect(goodFn).to.not.throw(Error)
    3. expect({ foo: 'baz'}).to.have.property('foo')
    4. .and.not.equal('bar')

    .deep

    设置deep标记,然后使用equalproperty断言。该标记可以让其后的断言不是比较对象本身,而是递归比较对象的键值对

    1. expect(foo).to.deep.equal({ bar: 'baz'})
    2. expect({ foo: { bar: { baz: 'quux'}}})
    3. .to.have.deep.property('foo.bar.baz', 'quux')

    deep.property中的特殊符号可以使用双反斜杠进行转义(第一个反斜杠是在字符串参数中对第二个反斜杠进行转义,第二个反斜杠用于在property中进行转义)

    1. var deepCss = { '.link': { '[target]': 42 } }
    2. expect(deepCss).to.have.deep.property('\\.link.\\[target\\]', 42)

    .any

    keys断言之前使用any标记(与all相反)

    1. expect(foo).to.have.any.keys('bar', 'baz')

    .all

    keys断言之前使用all标记(与any相反)

    1. expect(foo).to.have.all.keys('bar', 'baz')

    .a(type) / .an(type)

  • type:String,被测试的值的类型

aan断言即可作为语言链又可作为断言使用

  1. // 类型断言
  2. expect('test').to.be.a('string');
  3. expect({ foo: 'bar' }).to.be.an('object');
  4. expect(null).to.be.a('null');
  5. expect(undefined).to.be.an('undefined');
  6. expect(new Error).to.be.an('error');
  7. expect(new Promise).to.be.a('promise');
  8. expect(new Float32Array()).to.be.a('float32array');
  9. expect(Symbol()).to.be.a('symbol');
  10. // es6 overrides
  11. expect({[Symbol.toStringTag]:()=>'foo'}).to.be.a('foo');
  12. // language chain
  13. expect(foo).to.be.an.instanceof(Foo);

.include(value) / contains(value)

  • value:Object | String | Number

include()contains()即可作为属性类断言前缀语言链又可作为作为判断数组、字符串是否包含某值的断言使用。当作为语言链使用时,常用于key()断言之前

  1. expect([1, 2, 3]).to.include(2)
  2. expect('foobar').to.include('bar')
  3. expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo')

.ok

断言目标为真值。

  1. expect('everything').to.be.ok
  2. expect(1).to.be.ok
  3. expect(false).to.not.be.ok
  4. expect(null).to.not.be.ok

.true

断言目标为true,注意,这里与ok的区别是不进行类型转换,只能为true才能通过断言

  1. expect(true).to.be.true
  2. expect(1)to.not.be.true

.false

断言目标为false

  1. expect(false).to.be.false
  2. expect(0).to.not.be.false

.null

断言目标为null

  1. expect(null).to.be.null
  2. expect(undefined).to.not.be.null

.undefined

断言目标为undefined

  1. expect(undefine).to.be.undefined
  2. expect(null).to.not.be.undefined

.NaN

断言目标为非数字NaN

  1. expect('foo').to.be.null
  2. expect(4)to.not.be.null

.exist

断言目标存在,即非null也非undefined

  1. var foo = 'hi',
  2. bar = null,
  3. baz
  4. expect(foo).to.exist
  5. expect(bar).to.not.exist
  6. expect(baz).to.not.exist

.empty

断言目标的长度为0。对于数组和字符串,它检查length属性,对于对象,它检查可枚举属性的数量

  1. expect([]).to.be.empty
  2. expect('').to.be.empty
  3. expect({}).to.be.empty

.arguments

断言目标是一个参数对象arguments

  1. function test () {
  2. expect(arguments).to.be.arguments
  3. }

.equal(value)

  • value:Mixed

断言目标严格等于(===)value。另外,如果设置了deep标记,则断言目标深度等于value

  1. expect('hello').to.equal('hello')
  2. expect(42).to.equal(42)
  3. expect(1).to.not.equal(true)
  4. expect({ foo: 'bar'}).to.not.equal({ foo: 'bar'})
  5. expect({ foo: 'bar'}).to.deep.equal({foo: 'bar'})

.eql(value)

  • value:Mixed

断言目标深度等于value,相当于deep.equal(value)的简写

  1. expect({ foo: 'bar' }).to.eql({ foo: 'bar' })
  2. expect([1, 2, 3]).to.eql([1, 2, 3])

.above(value)

  • value: Number

断言目标大于(超过)value

  1. expect(10).to.be.above(5)

也可接在length后来断言一个最小的长度。相比直接提供长度的好处是提供了更详细的错误消息

  1. expect('foo').to.have.length.above(2)
  2. expect([1, 2, 3]).to.have.length.above(2)

.least(value)

  • value: Number

断言目标不小于(大于或等于)value

  1. expect(10).to.be.at.least(10)

也可接在length后来断言一个最小的长度。相比直接提供长度的好处是提供了更详细的错误消息

  1. expect('foo').to.have.length.of.at.least(3)
  2. expect([1, 2, 3]).to.have.length.of.at.least(3)

.below(value)

  • value:Number

断言目标小于value

  1. expect(5).to.be.below(10)

也可接在length后来断言一个最大的长度。相比直接提供长度的好处是提供了更详细的错误消息

  1. expect('foo').to.have.length.below(4)
  2. expect([1, 2, 3]).to.have.length.below(4)

.most(value)

  • value:String

断言目标不大于(小于或等于)value

  1. expect(5).to.be.at.most(5)

也可接在length后来断言一个最大的长度。相比直接提供长度的好处是提供了更详细的错误消息

  1. expect('foo').to.have.length.of.at.most(4)
  2. expect([1, 2, 3]).to.have.length.of.at.most(3)

.within(start, finish)

  • start:Number,下限
  • finish:Number,上限

断言目标在某个区间内

  1. expect(7).to.be.within(5, 10)

也可接在length后来断言一个长度区间。相比直接提供长度的好处是提供了更详细的错误消息

  1. expect('foo').to.have.length.within(2, 4)
  2. expect([1, 2, 3]).to.have.length.within(2, 4)

.instanceof(constructor)

  • constructor:Constructor,构造函数

断言目标是构造函数constructor的一个实例

  1. var Tea = function (name) { this.name = name },
  2. Chai = new Tea('chai')
  3. expect(Chai).to.be.an.instanceof(Tea)
  4. expect([1, 2, 3]).to.be.an.instanceof(Array)

.property(name, [value])

  • name:String,属性名
  • value:Mixed,可选,属性值

断言目标是否拥有某个名为name的属性,可选地如果提供了value则该属性值还需要严格等于(===value。如果设置了deep标记,则可以使用点.和中括号[]来指向对象和数组中的深层属性

  1. // 简单引用
  2. var obj = { foo: 'bar' }
  3. expect(obj).to.have.property('foo')
  4. expect(pbj).to.have.property('foo', 'bar')
  5. // 深层引用
  6. var deepObj = {
  7. green: { tea: 'matcha' },
  8. teas: [ 'Chai', 'matcha', { tea: 'konacha' } ]
  9. }
  10. expect(deepObj).to.have.deep.property('green.tea', 'matcha')
  11. expect(deepObj).to.have.deep.property('teas[1]', 'matcha')
  12. expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha')

如果目标是一个数组,还可以直接使用一个或多个数组下标作为name来在嵌套数组中断言deep.property

  1. var arr = [
  2. [ 'chai', 'matcha', 'konacha' ],
  3. [ { tea: 'chai' },
  4. { tea: 'matcha' },
  5. { tea: 'konacha' }
  6. ]
  7. ]
  8. expect(arr).to.have.deep.property('[0][1]', 'matcha')
  9. expect(arr).to.have.deep.property('[1][2].tea', 'konacha')

此外,property把断言的主语(subject)从原来的对象变为当前属性的值,使得可以在其后进一步衔接其它链式断言(来针对这个属性值进行测试)

  1. expect(obj).to.have.property('foo')
  2. .that.is.a('string')
  3. expect(deepObj).to.have.property('green')
  4. .that.is.an('object')
  5. .that.deep.equals({ tea: 'matcha' })
  6. expect(deepObj).to.have.property('teas')
  7. .that.is.an('array')
  8. .with.deep.property('[2]')
  9. .that.deep.equals({ tea: 'konacha' })

注意,只有当设置了deep标记的时候,在property() name中的点(.)和中括号([])才必须使用双反斜杠\进行转义(为什么是双反斜杠,在前文有提及),当没有设置deep标记的时候,是不能进行转义的

  1. // 简单指向
  2. var css = { '.link[target]': 42 }
  3. expect(css).to.have.property('.link[target]', 42)
  4. //深度指向
  5. var deepCss = { 'link': { '[target]': 42 } }
  6. expect(deepCss).to.have.deep.property('\\.link\\.[target]', 42)

.ownProperty(name)

  • name:String,属性名
    断言目标拥有名为name的自有属性
  1. expect('test').to.have.ownProperty('length')

.ownPropertyDescription(name[, descriptor])

  • name:String,属性名
  • descriptor: Object,描述对象,可选

断言目标的某个自有属性存在描述符对象,如果给定了descroptor描述符对象,则该属性的描述符对象必须与其相匹配

  1. expect('test').to.have.ownPropertyDescriptor('length')
  2. expect('test').to.have.ownPropertyDescriptor('length', {
  3. enumerable: false,
  4. configrable: false,
  5. writeable: false,
  6. value: 4
  7. })
  8. expect('test').not.to.have.ownPropertyDescriptor('length', {
  9. enumerable: false,
  10. configurable: false,
  11. writeable: false,
  12. value: 3
  13. })
  14. // 将断言的主语改为了属性描述符对象
  15. expect('test').to.have.ownPropertyDescriptor('length')
  16. .to.have.property('enumerable', false)
  17. expect('test').to.have.ownPropertyDescriptor('length')
  18. .to.have.keys('value')

.length

设置.have.length标记作为比较length属性值的前缀

  1. expect('foo').to.have.length.above(2)
  2. expect([1, 2, 3]).to.have.length.within(2, 4)

.lengthOf(value)

  • value:Number

断言目标的length属性为期望的值

  1. expect([1, 2, 3]).to.have.lengthOf(3)
  2. expect('foobar').to.have.lengthOf(6)

.match(regexp)

  • regexp:RegExp,正则表达式

断言目标匹配到一个正则表达式

  1. expect('foobar').to.match(/^foo/)

.string(string)

  • string:String,字符串

断言目标字符串包含另一个字符串

  1. expect('foobar').to.have.string('bar')

.keys(key1, [key2], […])

  • key:String | Array | Object 属性名

断言目标包含传入的属性名。与anyallcontains或者have前缀结合使用会影响测试结果:
当与any结合使用时,无论是使用have还是使用contains前缀,目标必须至少存在一个传入的属性名才能通过测试。注意,any或者all应当至少使用一个,否则默认为all
当结合allcontains使用时,目标对象必须至少拥有全部传入的属性名,但是它也可以拥有其它属性名
当结合allhave使用时,目标对象必须且仅能拥有全部传入的属性名

  1. // 结合any使用
  2. expect({ foo: 1, bar: 2, baz: 3 }).to.have.any.keys('foo', 'bar')
  3. expect({ foo: 1, bar: 2, baz: 3 }).to.contains.any.keys('foo', 'bar')
  4. // 结合all使用
  5. expect({ foo: 1, bar: 2, baz: 3 }).to.have.all.keys('foo', 'bar', 'baz')
  6. expect({ foo: 1, bar: 2, baz: 3 }).to.contains.all.keys('foo', 'bar')
  7. // 传入string
  8. expect({ foo: 1, bar: 2, baz: 3 }).to.have.any.keys('foo')
  9. // 传入Array
  10. expect({ foo: 1, bar: 2, baz: 3 }).to.have.all.keys(['foo', 'bar', 'baz'])
  11. // 传入Object
  12. expect({ foo: 1, bar: 2, baz: 3 }).to.have.any.keys({ bar: 2, foo: 1 })

.throw(constructor)

  • constructor: ErrorConstroctor | String | RegExp

断言目标函数会抛出一个指定错误或错误类型(使用instanceOf计算),也可使用正则表达式或者字符串来检测错误消息

  1. var err = new RefernceError('this is a bad function')
  2. var fn = function () { throw err }
  3. expect(fn).to.throw(ReferenceError)
  4. expect(fn).to.throw(Error)
  5. expect(fn).to.throw(/bad function/)
  6. expect(fn).to.not.throw('good function')
  7. expect(fn).to.throw(ReferrenceError, /bad function/)
  8. expect(fn).to.throw(err)

注意,当一个抛错断言被否定了(前面有.not),那么它会从Error构造函数开始依次检查各个可能传入的参数。检查一个只是消息类型不匹配但是已知的错误,合理的方式是先断言该错误存在,然后使用.and后断言错误消息不匹配

  1. expect(fn).to.throw(ReferenceError)
  2. .and.not.throw(/good function/)

.respondTo(method)

  • method:String

断言目标类或对象会响应一个方法(存在这个方法)

  1. Klass.prototype.bar = function () {}
  2. expect(Klass).to.respondTo('bar')
  3. expect(obj).to.respondTo('bar')

如果需要检查一个构造函数是否会响应一个静态方法(挂载在构造函数本身的方法),请查看itself标记

  1. Klass.baz = function () {}
  2. expect(Klass).itself.to.respondTo('baz')

.itself

设置itself标记,然后使用respondTo断言

  1. function Foo () {}
  2. Foo.bar = function () {}
  3. Foo.prototype.baz = function () {}
  4. expect(Foo).itself.to.respondTo('bar')
  5. expect(Foo).itself.not.to.respond('baz')

.satisfy(method)

  • method:Function,测试器,接受一个参数表示目标值,返回一个布尔值

断言目标值能够让给定的测试器返回真值

  1. expect(1).to.satisfy(function (num) { return num > 0 })

.closeTo(expected, delta)

  • expect:Numbre,期望值
  • delta:Numbre,范围半径

断言目标数字等于expected,或在期望值的+/-delta范围内

  1. expect(1.5).to.be.closeTo(1, 0.5)

.members(set)

  • set:Array

断言目标是set的超集,或前者有后者所有严格相等(===)的成员。另外,如果设置了deep标记,则成员进行深度比较(include/contains只能接受单个值,但它们的主语除了是数组,还可以判断字符串;members则将它们的能力扩展为能够接受一个数组,但主语只能是数组)

  1. expect([1, 2, 3]).to.include.members([3, 2])
  2. expect([1, 2, 3]).to.not.include.members([3, 2, 8])
  3. expect([4, 2]).to.have.members([2, 4])
  4. expect([5, 2]).to.not.have.members([5, 2, 1])
  5. expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }])

.oneOf(list)

  • list:Array

断言目标值出现在list数组的某个顶层位置(直接子元素,严格相等)

  1. expect('a').to.be.oneOf(['a', 'b', 'c'])
  2. expect(9).to.not.be.oneOf(['z'])
  3. // 严格相等,所以对象类的值必须为同一个引用才能被判定为相等
  4. var three = [3]
  5. expect([3]).to.not.be.oneOf([1, 2, [3]])
  6. expect(three).to.not.be.oneOf([1, 2, [3]])
  7. expect(three).to.be.oneOf([1, 2, three])

change(object, property)

  • object:Object,对象
  • property:String,属性名

断言目标方法会改变指定对象的指定属性

  1. var obj = { val: 10 }
  2. var fn = function () { obj.val += 3 }
  3. var noChangeFn = function () { return 'bar' + 'baz' }
  4. expect(fn).to.change(obj, 'val')

.increase(object, property)

  • object:Object,对象
  • property:String,属性名

断言目标方法会增加指定对象的属性

  1. var obj = { val: 10 }
  2. var fn = function () { obj.val = 15 }
  3. expect(fn).to.increase(obj, val)

.decrease(object, property)

  • object:Object,对象
  • property:String,属性名

断言目标方法会减少指定对象的属性

  1. var obj = { val: 10 }
  2. var fn = function () { obj.val = 5 }
  3. expect(fn).to.decrease(obj, val)

.extensible

断言目标对象是可扩展的(可以添加新的属性)

  1. var nonExtensibleObject = Object.preventExtensions({})
  2. var sealedObject = Object.seal({})
  3. var frozenObject = Object.freeze({})
  4. expect({}).to.be.extensible
  5. expect(nonExtensibleObject).to.not.be.extensible
  6. expect(sealObject).to.not.be.extensible
  7. expect(frozenObject).to.not.be.extensible

.sealed

断言目标对象是封闭的(无法添加新的属性并且存在的属性不能被删除但可以被修改)

  1. var sealedObject= Object.seal({})
  2. var frozenObject = Object.freeze({})
  3. expect(sealedObject).to.be.sealed
  4. expect(frozenObject).to.be.sealed
  5. expect({}).to.not.be.sealed

.frozen

断言目标对象是冻结的(无法添加新的属性并且存在的属性不能被删除和修改)

  1. var frozenObject = Object.freeze({})
  2. expect(frozenObject).to.be.frozen
  3. expect({}).to.not.be.frozen

来源