chai中文文档

原文档地址:https://www.chaijs.com/api/bdd/


BDD风格包含expect和should。它们以相同的链式结构进行断言,它们的不同之处在于初始的断言构造。可以查看风格指南来进行比较。


译注:风格指南中相关的翻译如下:
(鉴于有代码所以没有放入引用格式而是用分割线与正文区分)

BDD风格有两种风格:expect和should。两者都使用相同的可链接语言来构造断言,但它们在最初构造断言的方式上有所不同。在使用should的情况下将可能会产生一些问题,这也有一些方式去克服这些问题。
Expect: BDD风格暴露expect或should接口。在这两种情况下,你可以用自然语言的形式来链接断言。 Expect也允许你在任何可能发生的断言失败之前添加任意信息。当与布尔值、数字等非描述性主题一起连用时,这将十分好用。
Should: Should允许你使用与Expect接口相同的链式断言风格,然而当should风格启动链式断言时将可能在IE浏览器下存在一些问题,因此要注意浏览器兼容性。
两者间的区别:
首先要注意的是,expect的引入仅需要引入expect函数,而should的引入需要引入should的函数执行。

var chai = require('chai')
  , expect = chai.expect
  , should = chai.should();

expect接口提供了一个函数作为链接语言断言的起点。它适用于node.js和所有浏览器。
should接口实例化对象原型的产生单一实例来进行断言。它适用于node.js以及除Internet Explorer之外的所有现代浏览器。
Should需要额外注意的:
should通过实例化对象的原型进行工作,所以在一些情况下他将可能不会工作


API参照:

链式语言结构
使用以下的链式供给(getter)来提高你断言的可阅读性啊。

译注:以下的链式结构语句单纯是为了提高阅读性所加的一些自然语句。实际上是没有作用的。后续的api才是断言所用的接口。

Chains

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

.not
否定在其之后链接的所有断言。

expect(function () {}).to.not.throw();
expect({a: 1}).to.not.have.property('b');
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放在.deep链式之后将导致使用深度相等的方式来代替严格相等(===)

// 目标对象深度(但不严格) 等于 `{a: 1}`
expect({a: 1}).to.deep.equal({a: 1});
expect({a: 1}).to.not.equal({a: 1});

// 目标数组深度(但不严格) 包含 `{a: 1}`
expect([{a: 1}]).to.deep.include({a: 1});
expect([{a: 1}]).to.not.include({a: 1});

// 目标对象深度(但不严格) 包含 `x: {a: 1}`
expect({x: {a: 1}}).to.deep.include({x: {a: 1}});
expect({x: {a: 1}}).to.not.include({x: {a: 1}});

// 目标对象深度(但不严格) 含有成员 `{a: 1}`
expect([{a: 1}]).to.have.deep.members([{a: 1}]);
expect([{a: 1}]).to.not.have.members([{a: 1}]);

// 目标Set集深度(但不严格) 拥有键 `{a: 1}`
expect(new Set([{a: 1}])).to.have.deep.keys([{a: 1}]);
expect(new Set([{a: 1}])).to.not.have.keys([{a: 1}]);

// 目标对象深度(但不严格) 拥有属性 `x: {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');
expect({a: 1}).to.not.have.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断言需求以相同(译注:不加ordered时member断言值断言成员存在性而忽视顺序)的顺序断言其成员

expect([1, 2]).to.have.ordered.members([1, 2])
  .but.not.have.ordered.members([2, 1]);

当共同使用.include和.orderd时,排序开始于数组的首部。

expect([1, 2]).to.have.ordered.members([1, 2])
  .but.not.have.ordered.members([2, 1]);

.any
使得跟在其后的.key断言仅需求目标包含至少一个所给定的键名,它与需求目标满足所有所给键的.all断言是相反的。

expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd');

.all
使得跟在其后的.key断言仅需求目标需要包含所有所给的键名,它与仅需求目标包含至少一个所给定的键名.any断言是相反的。

expect({a: 1, b: 2}).to.have.all.keys('a', 'b');

需要注意的是当all和any都没有在链式之前添加的时候是默认使用all断言的,然而最好的方式通常是显式的加上all以提高断言的可读性。

.lengthOf(n[, msg]):
断言目标的length或size与给定值相同,接受参数msg在断言错误时给出提示。其也可以与.above、.below、.most、.lease、.within等断言连用。由于兼容性问题,lengthOf的别名length不能直接链接到未经经声明的方法如.a,,因此两者不可互换,应该用lengthOf来代替length。

.lengthOf接受当断言失败时的客户错误提示参数msg
该参数也可以作为expect的第二个参数给出。

expect([1, 2, 3]).to.have.lengthOf(2, 'nooo why fail??');
expect([1, 2, 3], 'nooo why fail??').to.have.lengthOf(2);

译注:当api中含有可选参数[msg]时均指当断言失败时的客户错误提示参数msg。此时当该断言出现失败时用户提示信息可以用以下两种形式给出。
expect(target).somechain.somechain.(val, [msg]);
expect(target, [msg]).somechain.somechain.(val);
后续api中会多次出现[msg]均为可在此处传入错误信息的含义。如果该api参数中不包含[msg]参数,则说明其仅接受错误信息参数作为expect的第二个参数被给出。

.match(re[, msg]):
断言目标与给定的正则规则相匹配。

.string(str[, msg]):
断言目标字符串包含所给子串,支持message在出错时给出用户信息。

.keys(key1[, key2[, …]]):
断言目标对象、数组、map集、set集包含所给键,只有目标的自身属性在所搜索范围内。当目标是一个对象或者一个数组时,键可以以一个或者多个字符串参数的形式提供,也可以是一个单一的数组参数或这一个单独的对象参数,当是后一种情况(单独的对象参数)时,仅考虑给定对象的键素材,而忽略它的值。当目标是一个set或者map时,每一个键都必须以参数分隔的形式提供。因为.key基于不同目标类型做了不同的事,所以在使用.key之前进行类型检测是非常重要的。
默认情况下,使用严格相等的方式比较set和map键,在链式中较早添加.deep可以使用深度相等的方式去替换它。
默认情况下目标必须含有所有给出的键,可以在链式之前加入.any使得目标仅需包含至少一个给出的键。需要注意的是当.all和.any都没有添加的时候默认是使用.all的,但是推荐显示的使用.all来提高阅读性。
当.include和.any连用的时候,仅.any生效

// 以下的两种断言是不同的
expect({a: 1}).to.have.any.keys('a', 'b’);
expect({a: 1}).to.include.any.keys('a', 'b');

其错误信息仅可以作为expect的第二个参数被给出。

.throw([errorLike], [errMsgMatcher], [msg])
当没有提供参数时,.throw调用目标函数并断言有一个错误将被抛出。当有一个参数被提供,并且该参数是一个错误构造器,.throw调用目标函数并断言有一个错误被抛出,该错误是所给构造器的一个实例。
当仅提供一个参数,且该参数是一个错误实例时,.throw调用目标函数并断言其产生一个与给定错误实例严格相等的错误。
当仅提供一个字符串参数时,.throw调用目标函数并断言其产生一个包含给定字符串信息的错误。
当仅提供一个正则表达式参数时,.throw调用目标函数并断言其产生一个符合所给正则表达式信息的错误。
注意一些常见的错误:
注意传入函数由.throw进行调用,而不是自己调用

expect(fn).to.throw(); // Good! Tests `fn` as desired
expect(fn()).to.throw(); // Bad! Tests result of `fn()`, not `fn`

另一个常见问题是当目标是一个对象的方法或者是一个依赖this独立运行的函数时,由于函数被.throw调用时会丢失原有的this环境而无法得知所谓的this到底指什么。关于这个问题目前有两种方案,第一种是将方法或函数在另一个函数中调用,另一种方式是使用bind进行绑定
.throws和.Throw可以与.throw互换

.respondTo(method[, msg])
当目标是一个非函数对象时, .respondTo断言该对象包含所提供方法名称的方法,该方法可以是自身的也可以是继承的,可以是可枚举也可以时不可枚举的。
当目标是一个函数时断言该函数原型属性又一个与所给名称相同的方法。同样的,该方法可以是自身的也可以是继承的,可以是可枚举也可以时不可枚举的。

.itself
强制链中的所有断言表现得好像目标是非函数对象,即使它是一个函数。因此,它导致断言目标具有给定名称的方法,而不是断言目标的原型属性具有给定名称的方法。

.satisfy(matcher[, msg])
将目标值作为所给matcher方法的第一个参数进行调用,并断言返回值为真。

.closeTo(expected, delta[, msg])
断言目标值是一个基于期待值(expected)+/- 差值(delta)范围内的数字。

.members(set[, msg])
断言目标数组与所给数组set具有相同成员。
默认情况下是断言成员严格相等,在链式之前增加.deep可以使使用深度相等的方式替代严格相等

// Target array deeply (but not strictly) has member `{a: 1}`
expect([{a: 1}]).to.have.deep.members([{a: 1}]);
expect([{a: 1}]).to.not.have.members([{a: 1}]);

默认情况下,顺序是不进行匹配的。在链式之前添加.ordered可以使得成员匹配按照相同的顺序进行。
expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]);
expect([1, 2, 3]).to.have.members([2, 1, 3]) .but.not.ordered.members([2, 1, 3]);
默认情况下,目标数组应与所给数组集保持相同size,在链式之前添加.include可以使得目标数组为期待数组的一个超集。需要注意的是在添加.include后,子集中重复的成员是会被忽略的
.deep、.include和.ordered都可以与其联合使用。值得注意的是,当链式中存在.include和.ordered时,期待数组与目标数组应该保持相同的顺序。
expect([{a: 1}, {b: 2}, {c: 3}]) .to.include.deep.ordered.members([{a: 1}, {b: 2}])
.but.not.include.deep.ordered.members([{b: 2}, {c: 3}]);

.oneOf(list[, msg])
断言目标时期待数组list的成员,但最好的方式时断言其为某个确认的值。
将会以严格相等的方式进行比较。

.change(subject[, prop[, msg]])
当一个参数被给定时。.change断言给定函数subject在目标函数之前调用和在目标函数之后调用的返回值不同。但更好的方式时断言函数会返回具体的某个值。
当有两个函数被提供时,.change断言对象subject的属性prop在目标函数调用前后的值是不同的。

.increase(subject[, prop[, msg]])
当一个参数被给定时。.increase断言给定函数subject在目标函数之后调用比在目标函数之前调用的返回值更大。但更好的方式是断言函数会增加具体的数值而不是断言他会增加任意数值。

var myObj = {val: 1}
  , subtractTwo = function () { myObj.val -= 2; };
expect(subtractTwo).to.decrease(myObj, 'val').by(2); // Recommended
expect(subtractTwo).to.not.increase(myObj, 'val'); // Not recommended

当有两个函数被提供时,.increase断言对象subject的属性prop在目标函数调用后比调用之前更大。

.decrease(subject[, prop[, msg]])
当一个参数被给定时。.decrease断言给定函数subject在目标函数之后调用比在目标函数之前调用的返回值更小。但更好的方式是断言函数会减少具体的数值而不是断言他会减少任意数值。

var val = 1
  , subtractTwo = function () { val -= 2; }
  , getVal = function () { return val; };

expect(subtractTwo).to.decrease(getVal).by(2); // Recommended
expect(subtractTwo).to.decrease(getVal); // Not recommended

当有两个函数被提供时,.increase断言对象subject的属性prop在目标函数调用后比调用之前更小。

.by(delta[, msg])
当.by跟在.increase链式断言之后,.by断言.increase断言的增量为给定的差值(delta)。
当.by跟在.decrease链式断言之后,.by断言.decrease断言的减少量为给定的差值(delta)。
当.by跟在.change链式断言之后,.by断言.change断言的差量为给定的差值(delta)。但这样做会产生问题,因为这将会产生一个不确定的期待,通常最好的做法是确认一个精准的期待输出,而只用精确输出去断言。

var myObj = {val: 1}
  , addTwo = function () { myObj.val += 2; }
  , subtractTwo = function () { myObj.val -= 2; };

expect(addTwo).to.increase(myObj, 'val').by(2); // 推荐的做法
expect(addTwo).to.change(myObj, 'val').by(2); // 不推荐的做法

expect(subtractTwo).to.decrease(myObj, 'val').by(2); // 推荐的做法
expect(subtractTwo).to.change(myObj, 'val').by(2); // 不推荐的做法

.extensible
断言目标是可扩展的,这意味着可以向其添加新属性。原始值是绝对不可扩展的。在.extensible链式结构之前添加.not可以否定.extensible.

译注:对象的可拓展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显示可拓展的,除非将它们转换为不可拓展的。同样,宿主对象的可拓展性也是由实现ECMAScript5的IavaScript引擎定义的。

var nonExtensibleObject = Object.preventExtensions({})
  , sealedObject = Object.seal({})
  , frozenObject = Object.freeze({});

expect(nonExtensibleObject).to.not.be.extensible;
expect(sealedObject).to.not.be.extensible;
expect(frozenObject).to.not.be.extensible;
expect(1).to.not.be.extensible;

其错误信息仅可以作为expect的第二个参数被给出。

expect(1, 'nooo why fail??').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;

在.sealed链式结构之前添加.not可以否定.sealed。

expect({a: 1}).to.not.be.sealed;

其错误信息仅可以作为expect的第二个参数被给出。

expect({a: 1}, 'nooo why fail??').to.be.sealed;

.frozen
断言目标是密封的,这意味着将无法加入新属性,并且已存在的属性无法被重新分配值,重新配置或者删除。原始值总是冻结的。

var frozenObject = Object.freeze({});

expect(frozenObject).to.be.frozen;
expect(1).to.be.frozen;

在.frozen链式结构之前添加.not可以否定.frozen。

expect({a: 1}).to.not.be.frozen;

其错误信息仅可以作为expect的第二个参数被给出。

expect({a: 1}, 'nooo why fail??').to.be.frozen;

.finite
断言目标是一个非NaN的数字,且该数字既不是正无穷也是负无穷。

expect(1).to.be.finite;

可以在链式之前增加.not进行否定,但这样的做法是危险的,因为这将会产生一个不确定的期待,它可能不是一个数字,或者是NaN,也可能是正无穷或者负无穷。所以通常最好的做法是期待它产生一个精确的输出,然后仅仅去断言可能会产生的期待。
当目标不被期待是一个数字是最好是去断言它是某个你期待的类型,而不是断言它是许多个不符合断言的类型之一。

expect('foo').to.be.a('string'); // Recommended
expect('foo').to.not.be.finite; // Not recommended

其错误信息仅可以作为expect的第二个参数被给出。

expect('foo', 'nooo why fail??').to.be.finite;

.fail([message])
.fail(actual, expected, [message], [operator])

抛出一个错误。

expect.fail();
expect.fail("custom error message");
expect.fail(1, 2);
expect.fail(1, 2, "custom error message");
expect.fail(1, 2, "custom error message", ">");
expect.fail(1, 2, undefined, ">");

译注:当仅有一个参数传入时抛出一个错误,将所传入信息作为错误信息。当传入三个参数时,分别 传入实际值,期待的值和错误提示,并将这个错误抛出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章