基於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 參考