学习Chai断言库

Chai是什么?

Chai是一个可以在node浏览器环境运行的BDD/TDD断言库,可以和任何JavaScript测试框架结合。

  • BDD(Behavior Driven Development行为驱动开发)
    • 是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作(做正确的事)
  • TDD(Test-Driven Development测试驱动开发)
    • 测试先于编写代码的思想用于指导软件开发(正确的做事)

安装

npm install chai
# Or
yarn add chai
复制代码

BDD

expectshould是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

判断目标不是nullundefined

expect(1).to.exist; 
复制代码

.empty

判断字符串数组length0,mapsetsize0,非函数对象自身没有属性

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限制,当使用如BabelTypeScript转换时.instanceof可能并不总是按预期工作。特别是,当子类化内置对象(如ArrayErrorMap)时,它可能会产生意外的结果。具体参考详细文档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
  1. 断言目标为object,array,mapsetkeys
  2. 默认会匹配所有的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])

别名 .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');
复制代码

后面暂时没有用到需要的可以看官方文档

转载于:https://juejin.im/post/5bea830de51d451e5a32f95a

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值