chai-iterator:用于可迭代对象的断言

Version Build Coverage Dependencies

内容

概述

chai-iterator 扩展了 Chai 断言库,提供用于测试 可迭代 对象的方法。在 ES2015 规范 中引入,可迭代对象具有一个 @@iterator 方法,它允许我们使用 for...of 循环遍历它们。许多 内置 类型默认情况下是可迭代的,而 自定义可迭代对象 也可以定义。chai-iterator 使测试所有此类对象变得容易。

您可能不需要 chai-iterator

在许多情况下,数组展开运算符 是测试可迭代对象的最佳方法。然而,chai-iterator 在测试非常长(或无限)可迭代对象的一部分时非常有用。

基本用法

以下是对我们使用 Chai Iterator 可以进行的断言的相当详尽的示例。虽然我们可以很容易地使用 expectassert,但我们将使用 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

断言

可迭代

断言目标是可迭代对象,即它具有 @@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)

断言目标产生 minmax 之间的 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

断言

参数

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] 许可。