<commit_before>---
layout:     post
title:      chai学习笔记
subtitle:   expect API学习
date:       2017-10-02 12:00:00
author:     DanteYu
tags:
    - chai
---

`Chai`是具有BDD/TDD风格的验证库，可以运行在node和浏览器环境中，一般和各类JavaScript框架一起使用。本文主要介绍在node环境中的使用。

为什么说`Chai`同时具有BDD或者TDD风格呢？因为`Chai`提供了不同风格的接口
* `should`和`expect`接口提供了BDD链式风格，是一种更加易读的风格
  * `tea.should.have.property('flavors').with.lengthOf(3);`
  * `expect(tea).to.have.property('flavors').with.lengthOf(3)`
* `assert`提供了更加经典的TDD验证风格
  * `assert.lengthOf(tea.flavors, 3);`

### 安装

安装可以使用`npm install chai`。更加推荐的做法是在`package.json`里面添加chai，然后使用`npm install`去安装。

```
"devDependencies": {
  "chai": "*"
}
```

使用`*`可以让每一次执行`npm install`的时候都去下载安装`Chai`的最新版本，这在持续集成里面能发挥很大的作用。

### chai.expect

让我们一起来看下如何使用`chai.expect`。

#### Chains Getter

下面所有的chains都可以用来增加验证表达式的可读性，读起来更接近自然语言。

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

#### .equal
`.equal`应该算是验证里面用得最多的方法了，用来验证两个对象是否相等，本质是用了strictly equal (===)。`.equal(val[,msg])`接受两个参数，第一个是期望值，第二个值是可选的当验证失败时会返回的错误消息。也可以使用`.eq`

典型用法
```js
expect(1).to.equal(1);
expect('foo').to.equal('foo');
expect(1).to.equal(2, 'this is error msg');
expect('foo').to.eq('foo');
```
当`.equal`与`.deep`一起使用的时候，使用的是[deep-eql](https://github.com/chaijs/deep-eql)而不是strictly equal (===)

```js
expect({a: 1}).to.equal({a: 1}); // fail
expect({a: 1}).to.deep.equal({a: 1}); //pass
```

#### .property
`.property(name[,val[,msg]])`用来判断对象里面的key是否存在，以及在提供第二个参数`val`的情况下验证该key对应的value值

```js
expect({a: 1}).to.have.property('a');
expect({a: 1}).to.have.property('a', 1);
```

当`.property`与`.deep`一起使用的时候，使用的是[deep-eql](https://github.com/chaijs/deep-eql)而不是strictly equal (===)

```js
expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); //pass
expect({x: {a: 1}}).to.have.property('x', {a: 1}); //fail
```
`.property`可以与`.own`一起使用来排除继承的属性

```js
Object.prototype.b = 2;

expect({a: 1}).to.have.own.property('a');
expect({a: 1}).to.have.own.property('a', 1);
expect({a: 1}).to.have.property('b').but.not.own.property('b');
```

`.property`可以与`.nested`一起使用来验证多层内嵌属性

```js
expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]');
expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]', 'y');
```
`.deep` & `.own` & `.nested`可以一起使用

```js
expect({x: {a: 1}}).to.have.deep.own.property('x', {a: 1});
expect({a: {b: [{c: 3}]}}).to.have.deep.nested.property('a.b[0]', {c: 3});
```

#### .include

`.include(val[,msg])`用来验证某种对象子集的包含关系

当对象是字符串的时候，用来判断参数`val`是否是目标字符串的子集 `expect('foobar').to.include('foo');`

当对象是数组的时候，用来判断参数`val`是否是数组的一员 `expect([1, 2, 3]).to.include(2);`

当对象是字典的时候，用来判断参数`val`是否是目标对象的子集 `expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});`

当对象是集合的时候，用来判断参数`val`是否是集合的一员 `expect([1, 2, 3]).to.include(2);`

当对象是`Map`的时候，用来判断参数`val`是某一对象的值 `expect(new Map([['a', 1], ['b', 2]])).to.include(2);`

一般来讲，因为`.include`会根据目标对象类型的不同而表现出不同的行为，我们通常在使用`.include`之前都会检查对象类型 `expect([1, 2, 3]).to.be.an('array').that.includes(2);`

默认情况下，strict (===) equality用来比较数组元素和对象属性，如果在链式结构里面加上`.deep`，使用的是[deep-eql](https://github.com/chaijs/deep-eql)
```js
expect([{a: 1}]).to.include({a: 1}); \\fail
expect([{a: 1}]).to.deep.include({a: 1}); \\pass
expect({x: {a: 1}}).to.include({x: {a: 1}}); \\fail
expect({x: {a: 1}}).to.deep.include({x: {a: 1}}) \\pass
```
`.include`可以与`.own`一起使用来排除继承的属性

```js
Object.prototype.b = 2;

expect({a: 1}).to.own.include({a: 1});
expect({a: 1}).to.include({a: 1}).but.not.own.include({b: 2});
```
`.deep`和`.own`可以一起使用 `expect({a: {b: 2}}).to.deep.own.include({a: {b: 2}});`

`.include`可以与`.nested`一起使用来验证多层内嵌属性 `expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'});`

`.deep`和`.nested`可以一起使用 `expect({a: {b: [{c:3}]}}).to.deep.nested.include({'a.b[0]': {c: 3}});`

如果`.`和`[]`是真实的属性名称，可以通过双反斜杠来进行转义`expect({'.a': {'[b]': 2}}).to.nested.include({'\\.a.\\[b\\]': 2});`

当目标对象是去验证参数`val`的key的时候，通常我们最好带上value一起做验证`expect({a: 3, b: 4}).to.include({a: 3, b: 4});`

`.include`的第二个可选参数msg是当验证失败时会显示的定制错误消息
```js
expect([1, 2, 3]).to.include(4, 'nooo why fail??');
expect([1, 2, 3], 'nooo why fail??').to.include(4);
```

当`.include`用作链式属性且与`.members`和`.keys`一起使用时，会去验证目标是不是期待对象的子集。

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

如果在`.key`之前加上`.any`就会抵消`.include`的作用

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

#### .members
#### .keys
#### .any
#### .all
#### .throw


#### .not

`.not`可以会进行否定式验证。

```js
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);
```
<commit_msg>add .all .any for chai post<commit_after>---
layout:     post
title:      chai学习笔记
subtitle:   expect API学习
date:       2017-10-02 12:00:00
author:     DanteYu
tags:
    - chai
---

`Chai`是具有BDD/TDD风格的验证库，可以运行在node和浏览器环境中，一般和各类JavaScript框架一起使用。本文主要介绍在node环境中的使用。

为什么说`Chai`同时具有BDD或者TDD风格呢？因为`Chai`提供了不同风格的接口
* `should`和`expect`接口提供了BDD链式风格，是一种更加易读的风格
  * `tea.should.have.property('flavors').with.lengthOf(3);`
  * `expect(tea).to.have.property('flavors').with.lengthOf(3)`
* `assert`提供了更加经典的TDD验证风格
  * `assert.lengthOf(tea.flavors, 3);`

### 安装

安装可以使用`npm install chai`。更加推荐的做法是在`package.json`里面添加chai，然后使用`npm install`去安装。

```
"devDependencies": {
  "chai": "*"
}
```

使用`*`可以让每一次执行`npm install`的时候都去下载安装`Chai`的最新版本，这在持续集成里面能发挥很大的作用。

### chai.expect

让我们一起来看下如何使用`chai.expect`。

#### Chains Getter

下面所有的chains都可以用来增加验证表达式的可读性，读起来更接近自然语言。

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

#### .equal
`.equal`应该算是验证里面用得最多的方法了，用来验证两个对象是否相等，本质是用了strictly equal (===)。`.equal(val[,msg])`接受两个参数，第一个是期望值，第二个值是可选的当验证失败时会返回的错误消息。也可以使用`.eq`

典型用法
```js
expect(1).to.equal(1);
expect('foo').to.equal('foo');
expect(1).to.equal(2, 'this is error msg');
expect('foo').to.eq('foo');
```
当`.equal`与`.deep`一起使用的时候，使用的是[deep-eql](https://github.com/chaijs/deep-eql)而不是strictly equal (===)

```js
expect({a: 1}).to.equal({a: 1}); // fail
expect({a: 1}).to.deep.equal({a: 1}); //pass
```

#### .property
`.property(name[,val[,msg]])`用来判断对象里面的key是否存在，以及在提供第二个参数`val`的情况下验证该key对应的value值

```js
expect({a: 1}).to.have.property('a');
expect({a: 1}).to.have.property('a', 1);
```

当`.property`与`.deep`一起使用的时候，使用的是[deep-eql](https://github.com/chaijs/deep-eql)而不是strictly equal (===)

```js
expect({x: {a: 1}}).to.have.deep.property('x', {a: 1}); //pass
expect({x: {a: 1}}).to.have.property('x', {a: 1}); //fail
```
`.property`可以与`.own`一起使用来排除继承的属性

```js
Object.prototype.b = 2;

expect({a: 1}).to.have.own.property('a');
expect({a: 1}).to.have.own.property('a', 1);
expect({a: 1}).to.have.property('b').but.not.own.property('b');
```

`.property`可以与`.nested`一起使用来验证多层内嵌属性

```js
expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]');
expect({a: {b: ['x', 'y']}}).to.have.nested.property('a.b[1]', 'y');
```
`.deep` & `.own` & `.nested`可以一起使用

```js
expect({x: {a: 1}}).to.have.deep.own.property('x', {a: 1});
expect({a: {b: [{c: 3}]}}).to.have.deep.nested.property('a.b[0]', {c: 3});
```

#### .include

`.include(val[,msg])`用来验证某种对象子集的包含关系

当对象是字符串的时候，用来判断参数`val`是否是目标字符串的子集 `expect('foobar').to.include('foo');`

当对象是数组的时候，用来判断参数`val`是否是数组的一员 `expect([1, 2, 3]).to.include(2);`

当对象是字典的时候，用来判断参数`val`是否是目标对象的子集 `expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});`

当对象是集合的时候，用来判断参数`val`是否是集合的一员 `expect([1, 2, 3]).to.include(2);`

当对象是`Map`的时候，用来判断参数`val`是某一对象的值 `expect(new Map([['a', 1], ['b', 2]])).to.include(2);`

一般来讲，因为`.include`会根据目标对象类型的不同而表现出不同的行为，我们通常在使用`.include`之前都会检查对象类型 `expect([1, 2, 3]).to.be.an('array').that.includes(2);`

默认情况下，strict (===) equality用来比较数组元素和对象属性，如果在链式结构里面加上`.deep`，使用的是[deep-eql](https://github.com/chaijs/deep-eql)
```js
expect([{a: 1}]).to.include({a: 1}); \\fail
expect([{a: 1}]).to.deep.include({a: 1}); \\pass
expect({x: {a: 1}}).to.include({x: {a: 1}}); \\fail
expect({x: {a: 1}}).to.deep.include({x: {a: 1}}) \\pass
```
`.include`可以与`.own`一起使用来排除继承的属性

```js
Object.prototype.b = 2;

expect({a: 1}).to.own.include({a: 1});
expect({a: 1}).to.include({a: 1}).but.not.own.include({b: 2});
```
`.deep`和`.own`可以一起使用 `expect({a: {b: 2}}).to.deep.own.include({a: {b: 2}});`

`.include`可以与`.nested`一起使用来验证多层内嵌属性 `expect({a: {b: ['x', 'y']}}).to.nested.include({'a.b[1]': 'y'});`

`.deep`和`.nested`可以一起使用 `expect({a: {b: [{c:3}]}}).to.deep.nested.include({'a.b[0]': {c: 3}});`

如果`.`和`[]`是真实的属性名称，可以通过双反斜杠来进行转义`expect({'.a': {'[b]': 2}}).to.nested.include({'\\.a.\\[b\\]': 2});`

当目标对象是去验证参数`val`的key的时候，通常我们最好带上value一起做验证`expect({a: 3, b: 4}).to.include({a: 3, b: 4});`

`.include`的第二个可选参数msg是当验证失败时会显示的定制错误消息
```js
expect([1, 2, 3]).to.include(4, 'nooo why fail??');
expect([1, 2, 3], 'nooo why fail??').to.include(4);
```

当`.include`用作链式属性且与`.members`和`.keys`一起使用时，会去验证目标是不是期待对象的子集。

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

如果在`.key`之前加上`.any`就会抵消`.include`的作用

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

#### .members
#### .keys
####. a
#### .any
`.any`使得所有`.keys`验证只要求期待对象包含至少一个指定的keys。与`.all`的作用相反。`  expect({a: 1, b: 2}).to.have.any.keys('a', 'd');`
#### .all
`.all`使得所有`.keys`验证只要求期待对象包含所有指定的keys。与`.any`的作用相反。`    expect({a: 1, b: 2}).to.have.any.keys('a', 'd');`

这是`.keys`默认地匹配方式，可以不显示地加上，但是一般加上的话易读性会更好。

#### .throw


#### .not

`.not`可以会进行否定式验证。

```js
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);
```

{'fraction_non_alphanumeric': 0.278123178550612, 'fraction_numerical': 0.025451719448222266, 'mean_word_length': 5.528218135700698, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6516006', 'n_tokens_mistral': 5770, 'n_tokens_neox': 5795, 'n_words': 681}