Chai是什么?
Chai
是一个可以在node
和浏览器
环境运行的BDD
/TDD
断言库,可以和任何JavaScript测试框架结合。
- BDD(Behavior Driven Development行为驱动开发)
- 是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作(
做正确的事
)
- 是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作(
- TDD(Test-Driven Development测试驱动开发)
- 测试先于编写代码的思想用于指导软件开发(
正确的做事
)
- 测试先于编写代码的思想用于指导软件开发(
安装
npm install chai
# Or
yarn add chai
复制代码
BDD
expect
和should
是BDD风格的。两者使用相同的语言链。
expect
使用构造函数来创建断言对象实例should
使用Object.prototype
提供一个getter
方法来实现,不兼容IE
var chai = require('chai')
, expect = chai.expect
, should = chai.should();
复制代码
语言链
下面语言链仅为了提高断言的可读性
- to
- be
- been
- is
- that
- which
- and
- has
- have
- with
- at
- of
- same
- but
- does
.not
否定所有断言
// 这个函数没有异常
expect(function () {}).to.not.throw();
// 这个对象没有b属性
expect({a: 1}).to.not.have.property('b');
// 它是一个数组,里面里面不包含3
expect([1, 2]).to.be.an('array').that.does.not.include(3);
复制代码
你可以使用.not
否定任何断言但一般不建议这样做。通常最好断言给出一个预期的值。
expect(2).to.equal(2); // 推荐
expect(2).to.not.equal(1); // 不推荐
复制代码
.deep
让.equal
,.include
,.members
,.keys
和,.property
去判断值相等,而不是严格的相等(===
)
expect({a: 1}).to.deep.equal({a: 1});
expect({a: 1}).to.not.equal({a: 1});
expect([{a: 1}]).to.deep.include({a: 1});
expect([{a: 1}]).to.not.include({a: 1});
expect({x: {a: 1}}).to.deep.include({x: {a: 1}});
expect({x: {a: 1}}).to.not.include({x: {a: 1}});
expect([{a: 1}]).to.have.deep.members([{a: 1}]);
expect([{a: 1}]).to.not.have.members([{a: 1}]);
expect(new Set([{a: 1}])).to.have.deep.keys([{a: 1}]);
expect(new Set([{a: 1}])).to.not.have.keys([{a: 1}]);
expect({x: {a: 1}}).to.have.deep.property('x', {a: 1});
expect({x: {a: 1}}).to.not.have.property('x', {a: 1});
复制代码
.nested
在所有的.property
和.include
使用.
和[]
语法
expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]');
expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'});
复制代码
如果原属性名称包含.
和[]
,需要使用\\
来转义
expect({'.a': {'[b]': 'x'}}).to.have.nested.property('\\.a.\\[b\\]');
expect({'.a': {'[b]': 'x'}}).to.nested.include({'\\.a.\\[b\\]': 'x'});
复制代码
.nested
不能和.own
结合使用
.own
.property
和.include
中忽略继承的属性
Object.prototype.b = 2;
expect({a: 1}).to.have.own.property('a');
expect({a: 1}).to.have.property('b').but.not.own.property('b');
expect({a: 1}).to.own.include({a: 1});
expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2});
复制代码
.own
不能和.nested
结合使用
.ordered
在.members
断言中保持相同的顺序
expect([1, 2]).to.have.ordered.members([1, 2])
.but.not.have.ordered.members([2, 1]);
复制代码
当.include
和.ordered
结合时,排序从数组头部开始
expect([1, 2, 3]).to.include.ordered.members([1, 2])
.but.not.include.ordered.members([2, 3]);
复制代码
.any
在.keys
之前使用包含任意一个
expect({ a: 1, b: 2 }).to.have.any.keys('b', 'd');
复制代码
.all
在.keys
之前使用包含所有
expect({ a: 1, b: 2 }).to.have.all.keys('a', 'b');
复制代码
.a(type[, msg])/.an(type[, msg])
- @param { String } type
- @param { String } msg optional
判断是什么类型
expect('foo').to.be.a('string');
expect({ a: 1 }).to.be.an('object');
expect(null).to.be.a('null');
expect(undefined).to.be.an('undefined');
expect(new Error).to.be.an('error');
expect(Promise.resolve()).to.be.a('promise');
expect(new Float32Array).to.be.a('float32array');
expect(Symbol()).to.be.a('symbol');
// 支持对象自定义类型,通过设置`Symbol.toStringTag`
var myObj = {
[Symbol.toStringTag]: 'myCustomType'
};
expect(myObj).to.be.a('myCustomType').but.not.an('object');
复制代码
如果使用.a
来断言类型一般放在前面。避免因为其它操作导致的异常。
expect([1, 2, 3]).to.be.an('array').that.includes(2);
expect([]).to.be.an('array').that.is.empty;
复制代码
.a
也可以当作语言链来提高可读性
expect({b: 2}).to.have.a.property('b');
复制代码
.include(val[, msg])
别名.includes
,.contain
和.contains
可以判断字符串
,数组中的成员
和对象的属性
expect('foobar').to.include('oo');
expect([1, 2, 3]).to.include(2);
expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});
复制代码
.ok
判断目标为真值(==
)
// 推荐使用
expect(1).to.equal(1);
// 不推荐使用
expect(1).to.be.ok;
// 推荐使用
expect(true).to.be.true;
// 不推荐使用
expect(true).to.be.ok;
复制代码
.true
判断目标为真
值(===
)
expect(true).to.be.true;
复制代码
.false
判断目标为假
值(===
)
expect(false).to.be.false;
复制代码
.null
判断目标为null
值(===
)
expect(null).to.be.null;
复制代码
.undefined
判断目标为undefined
值(===
)
expect(1).to.not.be.undefined;
复制代码
.NaN
判断目标为非数字
expect(NaN).to.be.NaN;
expect('foo').to.not.be.NaN;
复制代码
.exist
判断目标不是null
或undefined
expect(1).to.exist;
复制代码
.empty
判断字符串
或数组
的length
为0
,map
或set
的size
为0
,非函数对象
的自身没有属性
expect([]).to.be.empty;
expect('').to.be.empty;
expect(new Set()).to.be.empty;
expect(new Map()).to.be.empty;
expect({}).to.be.empty;
复制代码
.arguments/.Arguments
断言目标是一个arguments
对象
function test () {
expect(arguments).to.be.arguments;
}
test();
复制代码
.equal(val[, msg])
- @param { Mixed } val
- @param { String } msg optional
别名.equals
,.eq
和.equal
断言目标严格相等(===
)
expect(1).to.equal(1);
expect('foo').to.equal('foo');
复制代码
.eql(obj[, msg])
- @param { Mixed } val
- @param { String } msg optional
断言目标深度值,相当于(.deep.equal
)
expect({a: 1}).to.eql({a: 1}).but.not.equal({a: 1});
复制代码
.above(n[, msg])
- @param { Number } n
- @param { String } msg optional
断言目标值大于(n
),或者在链前面增加.lengthOf
来判断目标的length
expect(2).to.be.above(1);
expect('foo').to.have.lengthOf(3); // Recommended
expect('foo').to.have.lengthOf.above(2); // Not recommended
expect([1, 2, 3]).to.have.lengthOf(3); // Recommended
expect([1, 2, 3]).to.have.lengthOf.above(2); // Not recommended
复制代码
.least(n[, msg])
- @param { Number } n
- @param { String } msg optional
断言目标值大于或等于(n
),或者在链前面增加.lengthOf
来判断目标的length
expect(2).to.be.above(1);
expect(2).to.be.at.least(2);
expect('foo').to.have.lengthOf(3); // Recommended
expect('foo').to.have.lengthOf.at.least(2); // Not recommended
expect([1, 2, 3]).to.have.lengthOf(3); // Recommended
expect([1, 2, 3]).to.have.lengthOf.at.least(2); // Not recommended
复制代码
.below
与above
相反
expect(1).to.be.below(2);
复制代码
.most
与least
相反
expect(1).to.be.most(2);
expect(2).to.be.most(2);
复制代码
.within(start, finish[, msg])
- @param { Number } start lower bound inclusive
- @param { Number } finish upper bound inclusive
- @param { String } msg optional
判断目标在范围区间,或者在链前面增加.lengthOf
来判断目标的length
expect(2).to.be.within(1, 3);
expect(2).to.be.within(2, 3);
expect(2).to.be.within(1, 2);
expect('foo').to.have.lengthOf.within(2, 4);
expect([1, 2, 3]).to.have.lengthOf.within(2, 4);
复制代码
.instanceof(constructor[, msg])/.instanceOf(constructor[, msg])
- @param { Constructor } constructor
- @param { String } msg optional
通过constructor
判断是一个实例
function Cat () { }
expect(new Cat()).to.be.an.instanceof(Cat);
expect([1, 2]).to.be.an.instanceof(Array);
复制代码
由于ES5
限制,当使用如Babel
或TypeScript
转换时.instanceof
可能并不总是按预期工作。特别是,当子类化内置对象(如Array
,Error
和Map
)时,它可能会产生意外的结果。具体参考详细文档Babel ,TypeScript
.property(name[, val[, msg]])
- @param { String } name
- @param { Mixed } val (optional)
- @param { String } msg optional
断言目标有没有这个属性,val
有值判断该属性的值是不是相等(===
)
expect({a: 1}).to.have.property('a');
expect({ a: 1 }).to.have.property('a', 1);
// 该目标有a属性并且值是一个number类型
expect({ a: 1 }).to.have.property('a').that.is.a('number');
复制代码
.ownPropertyDescriptor(name[, descriptor[, msg]])
- @param { String } name
- @param { Object } descriptor optional
- @param { String } msg optional
别名.haveOwnPropertyDescriptor
断言目标属性是否存在,如果给定了descriptor
则必须符合规则
expect({ a: 1 }).to.have.ownPropertyDescriptor('a');
expect({ a: 1 }).to.have.ownPropertyDescriptor('a', {
configurable: true,
enumerable: true,
writable: true,
value: 1,
});
// .ownPropertyDescriptor将断言改为属性描述符对象
expect({ a: 1 }).to.have.ownPropertyDescriptor('a')
.that.has.property('enumerable', true);
复制代码
.lengthOf(n[, msg])
- @param { Number } n
- @param { String } msg optional
断言目标length
expect([1, 2, 3]).to.have.lengthOf(3);
expect('foo').to.have.lengthOf(3);
复制代码
.match(re[, msg])/.matches(re[, msg])
- @param { RegExp } re
- @param { String } msg optional
断言目标正则匹配
expect('foobar').to.match(/^foo/);
复制代码
.string(str[, msg])
- @param { String } str
- @param { String } msg optional
断言目标包含子串
expect('foobar').to.have.string('bar');
复制代码
.keys(key1[, key2[, …]])/.key(key1[, key2[, …]])
- @param { String | Array | Object } keys
- 断言目标为
object
,array
,map
和set
的keys
- 默认会匹配所有的
keys
,如果只需要匹配一个可以在前面加上.any
expect({a: 1, b: 2}).to.have.all.keys('a', 'b');
expect(['x', 'y']).to.have.all.keys(0, 1);
expect({a: 1, b: 2}).to.have.all.keys(['a', 'b']);
expect(['x', 'y']).to.have.all.keys([0, 1]);
expect({a: 1, b: 2}).to.have.all.keys({a: 4, b: 5}); // ignore 4 and 5
expect(['x', 'y']).to.have.all.keys({0: 4, 1: 5}); // ignore 4 and 5
// map和set会比较值(===)
expect(new Set([{ a: 1 }])).to.have.all.deep.keys([{ a: 1 }]);
expect({ a: 1, b: 2 }).to.have.any.keys('a');
复制代码
.throw([errorLike], [errMsgMatcher], [msg])
- @param { Error | ErrorConstructor } errorLike
- @param { String | RegExp } errMsgMatcher error message
- @param { String } msg optional
- @see developer.mozilla.org/en/JavaScri…
别名 .throws
,.Throw
断言目标抛出异常
var err = new TypeError('Illegal salmon!');
var badFn = function () { throw err; };
expect(badFn).to.throw();
expect(badFn).to.throw(TypeError);
expect(badFn).to.throw(err);
expect(badFn).to.throw('salmon');
expect(badFn).to.throw(/salmon/);
expect(badFn).to.throw(TypeError, 'salmon');
expect(badFn).to.throw(TypeError, /salmon/);
expect(badFn).to.throw(err, 'salmon');
复制代码
.respondTo(method[, msg])/.respondsTo(method[, msg])
- @param { String } method
- @param { String } msg optional
目标为非函数对象时,断言有没有方法
function Cat () {}
Cat.prototype.meow = function () {};
expect(new Cat()).to.respondTo('meow');
复制代码
目标是一个函数时,判断目标prototype
有没有改方法
function Cat () {}
Cat.prototype.meow = function () {};
expect(Cat).to.respondTo('meow');
复制代码
.itself
.respondTo
添加.itself
,判断它自身有没有该方法而不是从prototype
判断
function Cat() { }
Cat.prototype.meow = function () { };
Cat.hiss = function () { };
expect(Cat).itself.to.respondTo('hiss').but.not.respondTo('meow');
复制代码
.satisfy(matcher[, msg])
- @param { Function } matcher
- @param { String } msg optional
断言目标值在给定的函数中返回真值
expect(1).to.satisfy(function(num) {
return num > 0;
});
复制代码
.closeTo(expected, delta[, msg])/.approximately(expected, delta[, msg])
- @param { Number } expected
- @param { Number } delta
- @param { String } msg optional
断言目标值在expected
的+/-delta
范围内
expect(1).to.satisfy(function(num) {
return num > 0;
});
复制代码
.members(set[, msg])
- @param { Array } set
- @param { String } msg optional
// 断言目标数组具有相同的数组成员
expect([1, 2, 3]).to.have.members([2, 1, 3]);
expect([1, 2, 2]).to.have.members([2, 1, 2]);
// 成员比较使用===
expect([{a: 1}]).to.have.deep.members([{a: 1}]);
// 使用.ordered需比对顺序(不是排序比对)
expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]);
// 包含部分成员
expect([1, 2, 3]).to.include.members([1, 2]);
// 重复的元素将被忽略
expect([1, 2, 3]).to.include.members([1, 2, 2, 2]);
复制代码
.oneOf(list[, msg])
- @param { Array.<*> } list
- @param { String } msg optional
断言目标属于list
中的一个
expect(1).to.be.oneOf([1, 2, 3]);
复制代码
.change(object[, prop[, msg]])/.changes(object[, prop[, msg]])
- @param { Object } object
- @param { String } prop name optional
- @param { String } msg optional
断言属性是不是发生改变
var myObj = { dots: '' }
, addDot = function () { myObj.dots += '.'; };
expect(addDot).to.change(myObj, 'dots');
复制代码
.increase(subject[, prop[, msg]])/increases
- @param { Object } object
- @param { String } prop name optional
- @param { String } msg optional
断言目标属性值增加了多少
var myObj = {val: 1}
, addTwo = function () { myObj.val += 2; };
expect(addTwo).to.increase(myObj, 'val').by(2);
复制代码
.decrease(subject[, prop[, msg]])/.decreases
- @param { Object } object
- @param { String } prop name optional
- @param { String } msg optional
断言目标属性值减少了多少
var myObj = {val: 1}
, subtractTwo = function () { myObj.val -= 2; };
expect(subtractTwo).to.decrease(myObj, 'val').by(2);
复制代码
.by(delta[, msg])
- @param { Number } delta
- @param { String } msg optional
跟随.increase
时断言主语增加了多少
var myObj = {val: 1}
, addTwo = function () { myObj.val += 2; };
expect(addTwo).to.increase(myObj, 'val').by(2);
复制代码
.extensible
断言目标可扩展(可添加新属性)
expect({a: 1}).to.be.extensible;
复制代码
.sealed
断言目标是密封的(不能添加新属性,已存在属性不能修改和删除)
var sealedObject = Object.seal({});
var frozenObject = Object.freeze({});
expect(sealedObject).to.be.sealed;
expect(frozenObject).to.be.sealed;
expect(1).to.be.sealed;
复制代码
.frozen
断言目标是冻结的(不能添加新属性,已存在属性不能修改和删除)
var frozenObject = Object.freeze({});
expect(frozenObject).to.be.frozen;
expect(1).to.be.frozen;
复制代码
.finite
断言目标是一个数字不是Nan
和正负无穷大
expect(1).to.be.finite;
复制代码
TDD
除语法糖外,assert
和node.js的非常相似,assert
是三种断言风格唯一不支持链式调用的
assert(expression, message)
- @param { Mixed } 测试表达式为真
- @param { String } 显示错误消息
assert('foo' !== 'bar', 'foo is not bar');
assert(Array.isArray([]), 'empty arrays are arrays');
复制代码
.isOk(object, [message])
- @param { Mixed } object to test
- @param { String } message
断言object
为真
assert.isOk('everything', 'everything is ok');
assert.isOk(false, 'this will fail');
复制代码
.isOk(object, [message])
- @param { Mixed } object to test
- @param { String } message
断言object
为假
assert.isNotOk('everything', 'this will fail');
assert.isNotOk(false, 'this will pass');
复制代码
.equal(actual, expected, [message])
- @param { Mixed } actual
- @param { Mixed } expected
- @param { String } message
断言actual
==expected
assert.equal(3, '3', '== coerces values to strings');
复制代码
.notEqual(actual, expected, [message])
- @param { Mixed } actual
- @param { Mixed } expected
- @param { String } message
断言actual
!=expected
assert.notEqual(3, 4, 'these numbers are not equal');
复制代码
.strictEqual(actual, expected, [message])
- @param { Mixed } actual
- @param { Mixed } expected
- @param { String } message
断言actual
===expected
assert.strictEqual(true, true, 'these booleans are strictly equal');
复制代码
.notStrictEqual(actual, expected, [message])
- @param { Mixed } actual
- @param { Mixed } expected
- @param { String } message
断言actual
!==expected
assert.notStrictEqual(3, '3', 'no coercion for strict equality');
复制代码
.deepEqual(actual, expected, [message])
- @param { Mixed } actual
- @param { Mixed } expected
- @param { String } message
断言actual
对象与expected
对象属性值相等
assert.deepEqual({ tea: 'green' }, { tea: 'green' });
复制代码
.notDeepEqual(actual, expected, [message])
- @param { Mixed } actual
- @param { Mixed } expected
- @param { String } message
断言actual
对象与expected
对象属性值不相等
assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' });
复制代码
.isAbove(valueToCheck, valueToBeAbove, [message])
- @param { Mixed } valueToCheck
- @param { Mixed } valueToBeAbove
- @param { String } message
断言valueToCheck
>valueToBeAbove
assert.isAbove(5, 2, '5大于2');
复制代码
.isAtLeast(valueToCheck, valueToBeAbove, [message])
- @param { Mixed } valueToCheck
- @param { Mixed } valueToBeAbove
- @param { String } message
断言valueToCheck
>=valueToBeAbove
assert.isAtLeast(5, 2, '5大于等于2');
assert.isAtLeast(3, 3, '3大于等于3');
复制代码
.isBelow(valueToCheck, valueToBeAbove, [message])
- @param { Mixed } valueToCheck
- @param { Mixed } valueToBeAbove
- @param { String } message
断言valueToCheck
<valueToBeAbove
assert.isBelow(3, 6, '3 is strictly less than 6');
复制代码
.isAtMost(valueToCheck, valueToBeAbove, [message])
- @param { Mixed } valueToCheck
- @param { Mixed } valueToBeAbove
- @param { String } message
断言valueToCheck
>=valueToBeAbove
assert.isAtMost(3, 6, '3 is less than or equal to 6');
assert.isAtMost(4, 4, '4 is less than or equal to 4');
复制代码
.isTrue(value, [message])
- @param { Mixed } value
- @param { String } message
var teaServed = true;
assert.isTrue(teaServed, 'the tea has been served');
复制代码
.isNotTrue(value, [message])
- @param { Mixed } value
- @param { String } message
var tea = 'tasty chai';
assert.isNotTrue(tea, 'great, time for tea!');
assert.isTrue(teaServed, 'the tea has been served');
复制代码
后面暂时没有用到需要的可以看官方文档