chai-iterator:用于可迭代对象的断言
内容
概述
chai-iterator 扩展了 Chai 断言库,提供用于测试 可迭代 对象的方法。在 ES2015 规范 中引入,可迭代对象具有一个 @@iterator 方法,它允许我们使用 for...of 循环遍历它们。许多 内置 类型默认情况下是可迭代的,而 自定义可迭代对象 也可以定义。chai-iterator 使测试所有此类对象变得容易。
您可能不需要 chai-iterator
在许多情况下,数组展开运算符 是测试可迭代对象的最佳方法。然而,chai-iterator 在测试非常长(或无限)可迭代对象的一部分时非常有用。
基本用法
以下是对我们使用 Chai Iterator 可以进行的断言的相当详尽的示例。虽然我们可以很容易地使用 expect 或 assert,但我们将使用 Chai 的 should() 断言风格,仅仅为了与众不同。
[2, 3, 5].should.be.iterable;
[2, 3, 5].should.iterate.over([2, 3, 5]);
[2, 3, 5].should.iterate.from([2, 3]);
[2, 3, 5].should.iterate.until([3, 5]);
[2, 3, 5].should.iterate.for.lengthOf(3);
[2, 3, 5].should.iterate.for.length.above(2);
[2, 3, 5].should.iterate.for.length.below(4);
[2, 3, 5].should.iterate.for.length.of.at.least(3);
[2, 3, 5].should.iterate.for.length.of.at.most(3);
[2, 3, 5].should.iterate.for.length.within(2, 4);
[2, 3, 5].should.not.iterate.over([1, 2, 3]);
[{n: 2}, {n: 3}].should.deep.iterate.from([{n: 2}]);
我们不要局限于数组;我们可以测试任何可迭代对象。
'abcde'.should.iterate.until(['c', 'd', 'e']);
我们也可以将任何可迭代对象作为我们的预期值。
'abcde'.should.iterate.until('cde');
用户定义的可迭代对象
chai-iterator 最适合用于测试 用户定义的可迭代对象,例如以下 类 所构造的对象。
class Count {
  constructor(start=0, step=1) {
    this.start = start;
    this.step = step;
  }
  *[Symbol.iterator]() {
    for (let n = this.start; true; n += this.step) {
      yield n;
    }
  }
}
由 Count.prototype[@@iterator]() 生成的序列是无限的;它会无限期地继续产生值。尽管如此,我们仍然可以安全地使用 from() 断言,因为它将在我们的预期可迭代对象完成时终止。
let tens = new Count(10, 10);
tens.should.be.iterable;
tens.should.iterate.from([10, 20, 30]);
tens.should.iterate.from([10, 20, 30, 40, 50]);
只是不要尝试在无限序列上使用 over() 或 until()。前者将始终失败,而后者将永远不会停止。
生成器和迭代器
让我们生成 斐波那契数列。一个 生成器函数 只是一个返回 Generator 对象的函数——一个既是 迭代器 又是 可迭代 的对象。我们可以像测试任何其他可迭代对象一样测试 Generator。
function* fibonacci() {
  for (let [x, y] = [1, 1]; true; [x, y] = [y, x + y]) {
    yield x;
  }
}
fibonacci().should.iterate.from([1, 1, 2, 3, 5]);
但要小心。迭代器不能回到过去。一旦某个值被生成,它就永远丢失了。因此,以下断言通过。
let fiborator = fibonacci();
fiborator.should.iterate.from([1, 1, 2, 3, 5]);
fiborator.should.iterate.from([8, 13, 21, 34]);
通常,为每个断言构造一个新的 Generator 更有意义。
fibonacci().should.iterate.from([1, 1, 2, 3, 5]);
fibonacci().should.iterate.from([1, 1, 2, 3, 5, 8, 13]);
兼容性
chai-iterator 要求在环境中使用 Symbol.iterator。在 Node 中,这意味着版本必须为 v4.0 或更高版本。虽然 大多数浏览器 的最新版本是兼容的,但面向网络的项目几乎肯定应该使用 polyfill。
对于不支持 Symbol.iterator 的环境,Babel polyfill 是一个选项。更简洁的是,我们可以只从 core-js 库中获取两个子模块,如下所示。
require('core-js/es6/symbol');
require('core-js/fn/symbol/iterator');
安装
使用 npm 安装 chai-iterator。当然,还要确保安装 Chai。
npm install --save chai chai-iterator
设置
chai-iterator 可以作为 Node 模块、AMD 模块导入,或者包含在 HTML <script> 标签中。对于 TypeScript 用户,声明与包一起安装。
Node
要为 Node 设置 chai-iterator,请确保版本为 v4.0 或更高版本,因为之前的版本不支持 @@iterator 方法。
const chai = require('chai');
const chaiIterator = require('chai-iterator');
chai.use(chaiIterator);
AMD
chai-iterator 可以像这样在 AMD 模块中设置。
define((require, exports, module) => {
  let chai = require('chai');
  let chaiIterator = require('chai-iterator');
  chai.use(chaiIterator);
});
HTML 脚本标签
chai-iterator 可以通过 <script> 标签包含。如果它在 chai.js 之后加载,Chai 将自动使用它。
<script src="chai.js"></script>
<script src="chai-iterator.js"></script>
TypeScript
TypeScript 声明包含在包中。要使用它们,请确保使用 npm 安装 chai-iterator,然后通过 typings 安装声明及其依赖项。并且一定要安装 chai 的声明。
typings install --save-dev npm~chai npm:chai-iterator
在 编译器选项 中,将 "target" 设置为 "es6",或者至少包含对 lib.es6.d.ts 的引用。现在以下内容将正常工作。
import chai = require("chai");
import chaiIterator = require("chai-iterator");
chai.use(chaiIterator);
[2, 3, 5].should.iterate.over([2, 3, 5]);
Expect/Should API
断言
- 可迭代
- iterate.over()
- iterate.from()
- iterate.until()
- iterate.for.lengthOf()
- iterate.for.length.above()
- iterate.for.length.below()
- iterate.for.length.of.at.least()
- iterate.for.length.of.at.most()
- iterate.for.length.within()
可迭代
断言目标是可迭代对象,即它具有 @@iterator 方法。
expect([2, 3, 5]).to.be.iterable;
expect('abcdefg').to.be.iterable;
expect(12345).not.to.be.iterable;
iterate.over(expected)
断言目标遍历给定的值序列。设置 deep 标志以使用深度相等性来比较值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| expected | object | 可迭代对象。 | 
expect([2, 3, 5]).to.iterate.over([2, 3, 5]);
expect('abcdefg').to.itetate.over('abcdefg');
expect([2, 3, 5]).not.to.iterate.over([2, 3]);
expect([{n: 2}, {n: 3}]).to.deep.iterate.over([{n: 2}, {n: 3}]);
iterate.from(expected)
断言目标从给定的值序列开始迭代。设置 deep 标志以使用深度相等性来比较值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| expected | object | 可迭代对象。 | 
expect([2, 3, 5]).to.iterate.from([2, 3]);
expect('abcdefg').to.iterate.from('abc');
expect([2, 3, 5]).not.to.iterate.from([3, 5]);
expect([{n: 2}, {n: 3}]).to.deep.iterate.from([{n: 2}]);
iterate.until(expected)
断言目标以给定的值序列结束迭代。设置 deep 标志以使用深度相等性来比较值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| expected | object | 可迭代对象。 | 
expect([2, 3, 5]).to.iterate.until([3, 5]);
expect('abcdefg').to.iterate.until('efg');
expect([2, 3, 5]).not.to.iterate.until([2, 3]);
expect([{n: 2}, {n: 3}]).to.deep.iterate.until([{n: 3}]);
iterate.for.lengthOf(n)
断言目标恰好产生 n 个值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| n | number | 一个正整数 | 
expect([2, 3, 5]).to.iterate.for.lengthOf(3);
expect('abcdefg').to.iterate.for.lengthOf(7);
expect([2, 3, 5]).not.to.iterate.for.lengthOf(7);
iterate.for.length.above(n)
断言目标产生超过 n 个值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| n | number | 一个正整数 | 
expect([2, 3, 5]).to.iterate.for.length.above(2);
expect('abcdefg').to.iterate.for.length.above(5);
expect([2, 3, 5]).not.to.iterate.for.length.above(3);
iterate.for.length.below(n)
断言目标产生少于 n 个值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| n | number | 一个正整数 | 
expect([2, 3, 5]).to.iterate.for.length.below(4);
expect('abcdefg').to.iterate.for.length.below(10);
expect([2, 3, 5]).not.to.iterate.for.length.below(3);
iterate.for.length.of.at.least(n)
断言目标至少产生 n 个值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| n | number | 一个正整数 | 
expect([2, 3, 5]).to.iterate.for.length.of.at.least(2);
expect([2, 3, 5]).to.iterate.for.length.of.at.least(3);
expect([2, 3, 5]).not.to.iterate.for.length.of.at.least(4);
iterate.for.length.of.at.most(n)
断言目标最多产生 n 个值。
| 参数 | 类型 | 描述 | 
|---|---|---|
| n | number | 一个正整数 | 
expect([2, 3, 5]).to.iterate.for.length.of.at.most(4);
expect([2, 3, 5]).to.iterate.for.length.of.at.most(3);
expect([2, 3, 5]).not.to.iterate.for.length.of.at.most(2);
iterate.for.length.within(min, max)
断言目标产生 min 和 max 之间的 value,包括两者。
| 参数 | 类型 | 描述 | 
|---|---|---|
| min | number | 一个正整数 | 
| max | number | 一个正整数 | 
expect([2, 3, 5]).to.iterate.for.length.within(2, 4);
expect([2, 3, 5]).to.iterate.for.length.within(3, 3);
expect([2, 3, 5]).not.to.iterate.for.length.within(4, 7);
Assert API
断言
- isIterable()
- isNotIterable()
- iteratesOver()
- doesNotIterateOver()
- deepIteratesOver()
- doesNotDeepIterateOver()
- iteratesFrom()
- doesNotIterateFrom()
- deepIteratesFrom()
- doesNotDeepIterateFrom()
- iteratesUntil()
- doesNotIterateUntil()
- deepIteratesUntil()
- doesNotDeepIterateUntil()
- lengthOf()
参数
assert 方法的参数如下。
| 参数 | 类型 | 描述 | 
|---|---|---|
| value | any | 任何值。 | 
| expected | object | 可迭代对象。 | 
| n | number | 一个正整数。 | 
| message? | string | 可选消息,在出错时显示。 | 
isIterable(value, [message])
断言某个值是可迭代对象,即它是一个具有 @@iterator 方法的对象。
assert.isIterable([2, 3, 5]);
assert.isIterable('abcdefg');
isNotIterable(value, [message])
断言一个值不是一个可迭代对象,即它缺少一个 @@iterator 方法。
assert.isNotIterable(235);
assert.isNotIterable(true);
iteratesOver(value, expected, [message])
断言一个值精确地迭代给定的一系列值。
assert.iteratesOver([2, 3, 5], [2, 3, 5]);
assert.iteratesOver('abcdefg', 'abcdefg');
doesNotIterateOver(value, expected, [message])
断言一个值不精确地迭代给定的一系列值。
assert.doesNotIterateOver([2, 3, 5], [1, 2, 3]);
assert.doesNotIterateOver('abcdefg', 'abc');
deepIteratesOver(value, expected, [message])
断言一个值精确地迭代给定的一系列值,使用深度相等性。
assert.deepIteratesOver([{n: 2}, {n: 3}], [{n: 2}, {n: 3}]);
assert.deepIteratesOver([[0, 2], [1, 3]], [[0, 2], [1, 3]]);
doesNotDeepIterateOver(value, expected, [message])
断言一个值不精确地迭代给定的一系列值,使用深度相等性。
assert.doesNotDeepIterateOver([{n: 2}, {n: 3}], [{n: 5}, {n: 7}]);
assert.doesNotDeepIterateOver([[0, 2], [1, 3]], [[1, 3], [0, 2]]);
iteratesFrom(value, expected, [message])
断言一个值以给定的一系列值开始迭代。
assert.iteratesFrom([2, 3, 5], [2, 3, 5]);
assert.iteratesFrom([2, 3, 5], [2, 3]);
assert.iteratesFrom('abcdefg', 'abc');
assert.iteratesFrom('abcdefg', '');
doesNotIterateFrom(value, expected, [message])
断言一个值不以给定的一系列值开始迭代。
assert.doesNotIterateFrom([2, 3, 5], [3, 5]);
assert.doesNotIterateFrom('abcdefg', 'cdef');
deepIteratesFrom(value, expected, [message])
断言一个值以给定的一系列值开始迭代,使用深度相等性。
assert.deepIteratesFrom([{n: 2}, {n: 3}], [{n: 2}]);
assert.deepIteratesFrom([[0, 2], [1, 3]], [[0, 2]]);
doesNotDeepIterateFrom(value, expected, [message])
断言一个值不以给定的一系列值开始迭代,使用深度相等性。
assert.doesNotDeepIterateFrom([{n: 2}, {n: 3}], [{n: 5}]);
assert.doesNotDeepIterateFrom([[0, 2], [1, 3]], [[1, 3]]);
iteratesUntil(value, expected, [message])
断言一个值以给定的一系列值结束迭代。
assert.iteratesUntil([2, 3, 5], [2, 3, 5]);
assert.iteratesUntil([2, 3, 5], [3, 5]);
assert.iteratesUntil('abcdefg', 'efg');
assert.iteratesUntil('abcdefg', '');
doesNotIterateUntil(value, expected, [message])
断言一个值不以给定的一系列值结束迭代。
assert.doesNotIterateUntil([2, 3, 5], [2, 3]);
assert.doesNotIterateUntil('abcdefg', 'cdef');
deepIteratesUntil(value, expected, [message])
断言一个值以给定的一系列值结束迭代,使用深度相等性。
assert.deepIteratesUntil([{n: 2}, {n: 3}], [{n: 3}]);
assert.deepIteratesUntil([[0, 2], [1, 3]], [[1, 3]]);
doesNotDeepIterateUntil(value, expected, [message])
断言一个值不以给定的一系列值结束迭代,使用深度相等性。
assert.doesNotDeepIterateUntil([{n: 2}, {n: 3}], [{n: 5}]);
assert.doesNotDeepIterateUntil([[0, 2], [1, 3]], [[0, 2]]);
lengthOf(value, n, [message])
断言一个可迭代对象产生给定数量的值。如果value不是一个可迭代对象,或者它具有一个 'length' 属性,Chai 的内置 assert.lengthOf() 将被使用。
function* range(min=0, max=Infinity, step=1) {
  for (let n = min; n < max; n += step) {
    yield n;
  }
}
assert.lengthOf(range(0, 10), 10);
assert.lengthOf(range(6, 42), 36);
许可证
版权所有 © 2016–2017 Akim McMath。根据 [MIT 许可证][license] 许可。