Chai Immutable
该插件为 Chai 提供了一组针对 Facebook 的 JavaScript 集合不变库 的断言。
断言
- BDD API 参考(Expect / Should)
- TDD API 参考(Assert)
安装
Node.js
npm install --save-dev chai-immutable
yarn add --dev chai-immutable
然后你可以像使用其他 Chai 插件一样使用此插件
const chai = require('chai');
const chaiImmutable = require('chai-immutable');
chai.use(chaiImmutable);
ES6 语法(需要 Babel 编译)
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
chai.use(chaiImmutable);
在浏览器中
在包含 Chai 和 Immutable 之后包含此插件。它会自动插入到 Chai 中,并准备使用
<script src="chai-immutable.js"></script>
使用 chai-immutable 与其他插件
如果您将此插件与 chai-as-promised 或 dirty-chai 一起使用,请注意 chai-immutable 必须在它们之前加载。例如
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const chaiImmutable = require('chai-immutable');
const dirtyChai = require('dirty-chai');
const { expect } = chai;
chai.use(chaiImmutable);
chai.use(chaiAsPromised);
chai.use(dirtyChai);
const { List } = require('immutable');
/* ... */
expect(Promise.resolve(List.of(1, 2, 3))).to.eventually.have.size(3);
expect(true).to.be.true();
BDD API 参考(Expect / Should)
.empty
断言不变集合为空。
expect(List()).to.be.empty;
expect(List.of(1, 2, 3)).to.not.be.empty;
.equal(collection)
- @param { Collection } collection
断言目标的值等价于 collection 的值。Chai 原始 equal 方法的别名也被支持。
const a = List.of(1, 2, 3);
const b = List.of(1, 2, 3);
expect(a).to.equal(b);
不变数据结构应该只包含其他不变数据结构(与 Array 和 Object 不同),才能被视为不变,并能针对 .equal() 正确地工作。有关更多信息,请参见 issue #24。
此外,请注意,当针对不变数据结构进行测试时,deep.equal 和 eql 是 equal 的同义词,因此它们是 equal 的别名。
.referenceEqual(value)
- @param {Collection} value
断言目标的引用等价于 collection 的引用。此方法保留了 Chai 的 equal 的原始行为。
有关更多详细信息,请参见 issue #210。
const a = List.of(1, 2, 3);
const b = a;
const c = List.of(1, 2, 3);
expect(a).to.referenceEqual(b);
expect(a).to.not.referenceEqual(c);
.include(value)
- @param { Mixed } val
include 和 contain 断言可以用作基于属性的语言链,也可以用作方法来断言不变集合中包含某个值或子集。当用作语言链时,它们会为 keys 断言切换 contains 标志。
请注意,在不变数据结构的上下文中,deep.include 的行为与 include 完全相同。
expect(new List([1, 2, 3])).to.include(2);
expect(new List([1, 2, 3])).to.deep.include(2);
expect(new Map({ foo: 'bar', hello: 'world' })).to.include('bar');
expect(new Map({ a: 1, b: 2, c: 3 })).to.include(new Map({ a: 1, b: 2 }));
expect(new Map({ foo: 'bar', hello: 'world' })).to.include.keys('foo');
.keys(key1[, key2[, …]])
- 
    @param _{ String… Array Object Collection }_ keyN 
断言目标集合具有给定的键。
当目标是对象或数组时,键可以作为一个或多个字符串参数、单个数组参数、单个对象参数或不变集合提供。在最后两种情况下,只有给定对象/集合中的键很重要;值会被忽略。
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys('foo', 'bar');
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys(new List(['bar', 'foo']));
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys(new Set(['bar', 'foo']));
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys(new Stack(['bar', 'foo']));
expect(new List(['x', 'y'])).to.have.all.keys(0, 1);
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys(['foo', 'bar']);
expect(new List(['x', 'y'])).to.have.all.keys([0, 1]);
// Values in the passed object are ignored:
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys({ bar: 6, foo: 7 });
expect(new Map({ foo: 1, bar: 2 })).to.have.all.keys(
  new Map({ bar: 6, foo: 7 })
);
expect(new List(['x', 'y'])).to.have.all.keys({ 0: 4, 1: 5 });
请注意,在不变数据结构的上下文中,deep.property 的行为与 property 完全相同。
默认情况下,目标必须具有所有给定的键,并且不能更多。在链中提前添加 .any,仅要求目标至少具有一个给定的键。此外,在链中提前添加 .not 来否定 .keys。在否定 .keys 时,通常最好添加 .any,而在断言 .keys 时不进行否定,最好使用 .all。
当否定 .keys 时,.any 是首选,因为 .not.any.keys 断言了对输出的预期,而 .not.all.keys 会产生不确定的预期。
// Recommended; asserts that target doesn't have any of the given keys
expect(new Map({ a: 1, b: 2 })).to.not.have.any.keys('c', 'd');
// Not recommended; asserts that target doesn't have all of the given
// keys but may or may not have some of them
expect(new Map({ a: 1, b: 2 })).to.not.have.all.keys('c', 'd');
当断言 .keys 时不进行否定,.all 是首选,因为 .all.keys 断言了对输出的预期,而 .any.keys 会产生不确定的预期。
// Recommended; asserts that target has all the given keys
expect(new Map({ a: 1, b: 2 })).to.have.all.keys('a', 'b');
// Not recommended; asserts that target has at least one of the given
// keys but may or may not have more of them
expect(new Map({ a: 1, b: 2 })).to.have.any.keys('a', 'b');
请注意,当链中既没有 .all 也没有 .any 时,默认情况下使用 .all。但是,通常最好还是添加 .all,因为它可以提高可读性。
// Both assertions are identical
expect(new Map({ a: 1, b: 2 })).to.have.all.keys('a', 'b'); // Recommended
expect(new Map({ a: 1, b: 2 })).to.have.keys('a', 'b'); // Not recommended
在链中提前添加 .include,要求目标的键是预期键的超集,而不是相同的集合。
// Target object's keys are a superset of ['a', 'b'] but not identical
expect(new Map({ a: 1, b: 2, c: 3 })).to.include.all.keys('a', 'b');
expect(new Map({ a: 1, b: 2, c: 3 })).to.not.have.all.keys('a', 'b');
但是,如果 .any 和 .include 结合使用,则只生效 .any。在这种情况下,.include 会被忽略。
// Both assertions are identical
expect(new Map({ a: 1 })).to.have.any.keys('a', 'b');
expect(new Map({ a: 1 })).to.include.any.keys('a', 'b');
别名 .key 可以与 .keys 交换使用。
expect(new Map({ foo: 1 })).to.have.key('foo');
.property(path[, val])
- 
    @param _{ String Array Iterable }_ path 
- @param { Mixed } val (可选)
断言目标具有给定 path 的属性。
expect(new Map({ a: 1 })).to.have.property('a');
当提供 val 时,.property 还断言属性的值等于给定的 val。 val 可以是不可变集合。
expect(new Map({ a: 1 })).to.have.property('a', 1);
请注意,在不变数据结构的上下文中,deep.property 的行为与 property 完全相同。
在链中提前添加 .nested,在引用嵌套属性时启用点和括号表示法。不可变的 List 也可以用作 nested.property 的起点。
expect(Immutable.fromJS({ a: { b: ['x', 'y'] } })).to.have.nested.property(
  'a.b[1]'
);
expect(Immutable.fromJS({ a: { b: ['x', 'y'] } })).to.have.nested.property(
  'a.b[1]',
  'y'
);
expect(Immutable.fromJS({ a: { b: ['x', 'y'] } })).to.have.nested.property(
  ['a', 'b', 1],
  'y'
);
expect(Immutable.fromJS({ a: { b: ['x', 'y'] } })).to.have.nested.property(
  new List(['a', 'b', 1]),
  'y'
);
如果 . 或 [] 是实际属性名称的一部分,则可以通过在它们前面添加两个反斜杠来转义它们。
expect(Immutable.fromJS({ '.a': { '[b]': 'x' } })).to.have.nested.property(
  '\\.a.\\[b\\]'
);
在链中提前添加 .not 来否定 .property。
expect(new Map({ a: 1 })).to.not.have.property('b');
但是,在提供 val 时,否定 .property 很危险。问题在于它会产生不确定的预期,断言目标要么没有给定 path 的属性,要么它确实具有给定键 path 的属性,但其值不等于给定的 val。通常最好确定预期的确切输出,然后编写只接受该确切输出的断言。
当目标预期在给定的 path 没有属性时,通常最好断言这一点。
expect(new Map({ b: 2 })).to.not.have.property('a'); // Recommended
expect(new Map({ b: 2 })).to.not.have.property('a', 1); // Not recommended
当目标预期在给定的键 path 具有属性时,通常最好断言该属性具有其预期值,而不是断言它没有多个意外值之一。
expect(new Map({ a: 3 })).to.have.property('a', 3); // Recommended
expect(new Map({ a: 3 })).to.not.have.property('a', 1); // Not recommended
.property 会更改链中后续任何断言的目标,使其成为原始目标对象中属性的值。
expect(new Map({ a: 1 }))
  .to.have.property('a')
  .that.is.a('number');
.size(value)
- @param { Number } size
断言不可变集合具有预期的规模。
expect(List.of(1, 2, 3)).to.have.size(3);
它也可以用作链的前驱,以进行 size 属性的值比较。
expect(List.of(1, 2, 3)).to.have.size.least(3);
expect(List.of(1, 2, 3)).to.have.size.most(3);
expect(List.of(1, 2, 3)).to.have.size.above(2);
expect(List.of(1, 2, 3)).to.have.size.below(4);
expect(List.of(1, 2, 3)).to.have.size.within(2, 4);
与 length/lengthOf 类似,sizeOf 是 size 的别名
expect(List.of(1, 2, 3)).to.have.sizeOf(3);
TDD API 参考(Assert)
.equal(actual, expected)
- @param { Collection } actual
- @param { Collection } expected
断言 actual 的值等效于 expected 的值。请注意,在不可变数据结构的上下文中,.strictEqual() 和 .deepEqual() 的断言方式与 .equal() 完全相同。
const a = List.of(1, 2, 3);
const b = List.of(1, 2, 3);
assert.equal(a, b);
不可变数据结构应该只包含其他不可变数据结构(与 Array 和 Object 不同),才能被视为不可变数据结构,并正确地针对 .equal()、.strictEqual() 或 .deepEqual() 工作。有关更多信息,请参见 issue #24。
.referenceEqual(actual, expected)
- @param {Collection} actual
- @param {Collection} expected
断言 actual 的引用等效于 expected 的引用。此方法保留了 Chai 的 equal 的原始行为。
有关更多详细信息,请参见 issue #210。
const a = List.of(1, 2, 3);
const b = a;
const c = List.of(1, 2, 3);
assert.referenceEqual(a, b);
assert.throws(() => assert.referenceEqual(a, c));
.notEqual(actual, expected)
- @param { Collection } actual
- @param { Collection } expected
断言 actual 的值不等效于 expected 的值。请注意,在不可变数据结构的上下文中,.notStrictEqual() 和 .notDeepEqual() 的断言方式与 .notEqual() 完全相同。
const a = List.of(1, 2, 3);
const b = List.of(4, 5, 6);
assert.notEqual(a, b);
.notReferenceEqual(actual, expected)
- @param {Collection} actual
- @param {Collection} expected
断言 actual 的引用不等效于 expected 的引用。此方法保留了 Chai 的 notEqual 的原始行为。
有关更多详细信息,请参见 issue #210。
const a = List.of(1, 2, 3);
const b = a;
const c = List.of(1, 2, 3);
assert.throws(() => assert.notReferenceEqual(a, b));
assert.notReferenceEqual(a, c);
.sizeOf(collection, length)
- @param { Collection } collection
- @param { Number } size
断言不可变集合具有预期的规模。
assert.sizeOf(List.of(1, 2, 3), 3);
assert.sizeOf(new List(), 0);