前端单元测试 -- chai

风里有诗句

介绍

chai 是一个断言库,可以与任何javascript测试框架完美地配对。支持BDD和TDD两种风格的断言形式,具有链功能的BDD样式提供了一种表达性强的语言和易于阅读的样式,而TDD断言样式则提供了更古典的感觉:

  • should: BDD风格断言
  • expect: BDD风格断言
  • assert: TDD风格断言

个人建议使用expect,在这里只介绍expect

安装

npm install chai

语言链

以下是可链接的获取器,以提高断言的可读性。

  • 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); // Recommended
expect(2).to.not.equal(1); // Not recommended 

.deep

导致链中后面的所有.equal,.include,.members,.keys和.property断言使用深度相等而不是严格 === 相等。

// Target object deeply (but not strictly) equals `{a: 1}`
expect({a: 1}).to.deep.equal({a: 1});
expect({a: 1}).to.not.equal({a: 1});

// Target array deeply (but not strictly) includes `{a: 1}`
expect([{a: 1}]).to.deep.include({a: 1});
expect([{a: 1}]).to.not.include({a: 1});

// Target object deeply (but not strictly) includes `x: {a: 1}`
expect({x: {a: 1}}).to.deep.include({x: {a: 1}});
expect({x: {a: 1}}).to.not.include({x: {a: 1}});

// 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}]);

// Target set deeply (but not strictly) has key `{a: 1}`
expect(new Set([{a: 1}])).to.have.deep.keys([{a: 1}]);
expect(new Set([{a: 1}])).to.not.have.keys([{a: 1}]);

// Target object deeply (but not strictly) has property `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

使链中后面的所有.member断言要求成员的顺序相同。

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断言仅要求目标具有至少一个给定的键。
这与.all相反,后者要求目标具有所有给定的键。

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

有关何时使用.any或.all的指南,请参见.keys文档。

.all

导致链中后面的所有.keys断言要求目标具有所有给定的键。
这与.any相反,后者仅要求目标具有至少一个给定的键。

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

请注意,当.all和.any都未在链中的早期添加时,默认情况下将使用.all。
但是,通常最好还是添加.all,因为它可以提高可读性.

有关何时使用.any或.all的指南,请参见.keys文档。

.a(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');

.a支持通过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;

在链的前面添加.not,以取反.a
但是,通常最好断言目标是预期的类型,而不是断言它不是许多意外类型之一。

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

.a接受一个可选的msg参数,这是一个自定义错误消息,以显示断言失败的时间。
该消息也可以作为期望的第二个参数给出。

expect(1).to.be.a('string', 'nooo why fail??');
expect(1, 'nooo why fail??').to.be.a('string');

.a也可以用作语言链来提高断言的可读性。

expect({b: 2}).to.have.a.property('b');

别名.an可以与.a互换使用。

.include(val[, msg])

  • @param { Mixed } val
  • @param { String } msg optional

当目标是字符串时,.include断言给定的字符串val是目标的子字符串。

expect('foobar').to.include('foo');

当目标是数组时,.include断言给定的val是目标的成员。

expect([1, 2, 3]).to.include(2);

当目标是对象时,.include断言给定对象val的属性是目标属性的子集。

expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});

当目标是Set或WeakSet时,.include断言给定val是目标的成员。使用SameValueZero相等算法。

expect(new Set([1, 2])).to.include(2);

当目标是Map时,.include断言给定的val是目标的值之一。使用SameValueZero相等算法。

expect(new Map([['a', 1], ['b', 2]])).to.include(2);

由于.include会根据目标的类型执行不同的操作,因此在使用.include之前检查目标的类型很重要。有关测试目标类型的信息,请参阅.a文档。

expect([1, 2, 3]).to.be.an('array').that.includes(2);

默认情况下,使用严格(===)相等性比较数组成员和对象属性。
在链的前面添加.deep,以改为使用深度相等(不支持WeakSet目标)。

// Target array deeply (but not strictly) includes `{a: 1}`
expect([{a: 1}]).to.deep.include({a: 1});
expect([{a: 1}]).to.not.include({a: 1});

// Target object deeply (but not strictly) includes `x: {a: 1}`
expect({x: {a: 1}}).to.deep.include({x: {a: 1}});
expect({x: {a: 1}}).to.not.include({x: {a: 1}});

默认情况下,使用对象时会搜索目标的所有属性。这包括继承和/或不可枚举的属性。在链的前面添加.own可以从搜索中排除目标的继承属性。

Object.prototype.b = 2;

expect({a: 1}).to.own.include({a: 1});
expect({a: 1}).to.include({b: 2}).but.not.own.include({b: 2});

请注意,始终仅在目标对象中搜索val自己的可枚举属性。

.deep和.own可以组合使用。

expect({a: {b: 2}}).to.deep.own.include({a: {b: 2}});

在引用嵌套属性时,在链的前面添加.nested以启用点和括号符号。

expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'});

如果 .[] 是实际属性名称的一部分,可以通过在其前面添加两个反斜杠来对其进行转义。

expect({'.a': {'[b]': 2}}).to.nested.include({'\\.a.\\[b\\]': 2});

.deep和.nested可以组合使用。

expect({a: {b: [{c: 3}]}}).to.deep.nested.include({'a.b[0]': {c: 3}});

.own和.nested不能合并。

在链的前面添加.not,以抵消.include。

expect('foobar').to.not.include('taco');
expect([1, 2, 3]).to.not.include(4);

但是,当目标是对象时,否定.include是很危险的。问题在于,它断言目标对象没有val的所有键/值对,但可能有也可能没有,因此产生了不确定的期望。通常最好先确定期望的确切输出,然后编写仅接受该精确输出的断言。

当甚至不希望目标对象具有val键时,通常最好准确地断言。

expect({c: 3}).to.not.have.any.keys('a', 'b'); // Recommended
expect({c: 3}).to.not.include({a: 1, b: 2}); // Not recommended

当期望目标对象具有val的键时,通常最好断言每个属性都有其期望值,而不是断言每个属性都没有许多意外值之一。

expect({a: 3, b: 4}).to.include({a: 3, b: 4}); // Recommended
expect({a: 3, b: 4}).to.not.include({a: 1, b: 2}); // Not recommended

.include接受一个可选的msg参数,这是一个自定义错误消息,以显示断言失败的时间。该消息也可以作为期望的第二个参数给出。

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

.include也可以用作语言链,导致链中的所有.members和.keys断言要求目标是预期集合的超集,而不是同一集合。请注意,添加.include时,.members会忽略子集中的重复项。

// Target object's keys are a superset of ['a', 'b'] but not identical
expect({a: 1, b: 2, c: 3}).to.include.all.keys('a', 'b');
expect({a: 1, b: 2, c: 3}).to.not.have.all.keys('a', 'b');

// Target array is a superset of [1, 2] but not identical
expect([1, 2, 3]).to.include.members([1, 2]);
expect([1, 2, 3]).to.not.have.members([1, 2]);

// Duplicates in the subset are ignored
expect([1, 2, 3]).to.include.members([1, 2, 2, 2]);

请注意,在链中的前面添加.any会导致.keys断言忽略.include。

// Both assertions are identical
expect({a: 1}).to.include.any.keys('a', 'b');
expect({a: 1}).to.have.any.keys('a', 'b');

别名.includes,.contain和.contains可以与.include互换使用。

.ok

断言目标是真实值(在布尔上下文中认为是真实)。
但是,通常最好断言该目标严格(===)或深等于其预期值。

expect(1).to.equal(1); // Recommended
expect(1).to.be.ok; // Not recommended

expect(true).to.be.true; // Recommended
expect(true).to.be.ok; // Not recommended

在链的前面添加.not即可取消.ok。

expect(0).to.equal(0); // Recommended
expect(0).to.not.be.ok; // Not recommended

expect(false).to.be.false; // Recommended
expect(false).to.not.be.ok; // Not recommended

expect(null).to.be.null; // Recommended
expect(null).to.not.be.ok; // Not recommended

expect(undefined).to.be.undefined; // Recommended
expect(undefined).to.not.be.ok; // Not recommended

可以将自定义错误消息作为预期的第二个参数。

expect(false, 'nooo why fail??').to.be.ok; 

.true

断言目标严格(===)等于true。

expect(true).to.be.true;

在链的前面添加.not即可取消.true。但是,通常最好断言目标等于其期望值,而不是不等于真实值。

expect(false).to.be.false; // Recommended
expect(false).to.not.be.true; // Not recommended

expect(1).to.equal(1); // Recommended
expect(1).to.not.be.true; // Not recommended

可以将自定义错误消息作为预期的第二个参数。

expect(false, 'nooo why fail??').to.be.true; 

.false

断言目标严格(===)等于false。

expect(null).to.be.false;

.null

断言目标严格(===)等于null。

expect(null).to.be.null;

.undefined

断言目标严格(===)等于undefined。

expect(undefined).to.be.undefined;

.NaN

断言目标严格(===)等于NaN。

expect(NaN).to.be.NaN;

.exist

断言目标不严格等于(===)null或未定义。但是,通常最好断言目标等于其期望值。

expect(1).to.equal(1); // Recommended
expect(1).to.exist; // Not recommended

expect(0).to.equal(0); // Recommended
expect(0).to.exist; // Not recommended

.empty

当目标是字符串或数组时,.empty断言目标的length属性严格(===)等于0。

expect([]).to.be.empty;
expect('').to.be.empty;

当目标是地图或集合时,.empty断言目标的size属性严格等于0。

expect(new Set()).to.be.empty;
expect(new Map()).to.be.empty;

当目标是非功能对象时,.empty断言目标没有任何自己的可枚举属性。具有基于符号的键的属性将从计数中排除。

expect({}).to.be.empty;

由于.empty会根据目标的类型执行不同的操作,因此在使用.empty之前检查目标的类型很重要。有关测试目标类型的信息,请参阅.a文档。

expect([]).to.be.an('array').that.is.empty;

在链的前面添加.not即可取消.empty。但是,通常最好断言目标包含其预期的值数,而不是断言目标值不为空。

expect([1, 2, 3]).to.have.lengthOf(3); // Recommended
expect([1, 2, 3]).to.not.be.empty; // Not recommended

expect(new Set([1, 2, 3])).to.have.property('size', 3); // Recommended
expect(new Set([1, 2, 3])).to.not.be.empty; // Not recommended

expect(Object.keys({a: 1})).to.have.lengthOf(1); // Recommended
expect({a: 1}).to.not.be.empty; // Not recommended
阅读 1.1k
29 声望
3 粉丝
0 条评论
29 声望
3 粉丝
文章目录
宣传栏