Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions docs/en/guides/testing-async-components.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Testing Asynchronous Behavior
# 测试异步行为

To simplify testing, `vue-test-utils` applies DOM updates _synchronously_. However, there are some techniques you need to be aware of when testing a component with asynchronous behavior such as callbacks or promises.
为了让测试变得简单,`vue-test-utils` _同步_应用 DOM 更新。不过当测试一个带有回调或 Promise 等异步行为的组件时,你需要留意一些技巧。

One of the most common asynchronous behaviors is API calls and Vuex actions. The following examples shows how to test a method that makes an API call. This example uses Jest to run the test and to mock the HTTP library `axios`. More about Jest manual mocks can be found [here](https://facebook.github.io/jest/docs/en/manual-mocks.html#content).
API 调用和 Vuex action 都是最常见的异步行为之一。下列例子展示了如何测试一个会调用到 API 的方法。这个例子使用 Jest 运行测试用例同时模拟了 HTTP `axios`。更多关于 Jest 的手动模拟的介绍可移步[这里](https://facebook.github.io/jest/docs/en/manual-mocks.html#content)

The implementation of the `axios` mock looks like this:
`axios` 的模拟实现大概是这个样子的:

``` js
export default {
Expand All @@ -14,7 +14,7 @@ export default {
}
```

The below component makes an API call when a button is clicked, then assigns the response to `value`.
下面的组件在按钮被点击的时候会调用一个 API,然后将响应的值赋给 `value`

``` html
<template>
Expand All @@ -41,7 +41,7 @@ The below component makes an API call when a button is clicked, then assigns the
</script>
```

A test can be written like this:
测试用例可以写成像这样:

``` js
import { shallow } from 'vue-test-utils'
Expand All @@ -57,7 +57,7 @@ test('Foo', () => {
})
```

This test currently fails because the assertion is called before the promise in `fetchResults` resolves. Most unit test libraries provide a callback to let the runner know when the test is complete. Jest and Mocha both use `done`. We can use `done` in combination with `$nextTick` or `setTimeout` to ensure any promises resolve before the assertion is made.
现在这则测试用例会失败,因为断言在 `fetchResults` 中的 Promise 完成之前就被调用了。大多数单元测试库都提供一个回调来使得运行期知道测试用例的完成时机。Jest Mocha 都是用了 `done`。我们可以和 `$nectTick` 或 `setTimeout` 结合使用 `done` 来确保任何 Promise 都会在断言之前完成。

``` js
test('Foo', () => {
Expand All @@ -72,9 +72,9 @@ test('Foo', () => {
})
```

The reason `$nextTick` or `setTimeout` allow the test to pass is because the microtask queue where promise callbacks are processed run before the task queue, where `$nextTick` and `setTimeout` are processed. This means by the time the `$nexTick` and `setTimeout` run, any promise callbacks on the microtask queue will have been executed. See [here](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) for a more detailed explanation.
`$nextTick` `setTimeout` 允许测试通过的原因是 Promise 回调的 microtask 队列会在处理 `$nextTick` `setTimeout` 的任务队列之前先被处理。也就是说在 `$nextTick` 和 `setTimeout` 运行的时候,任何 microtask 队列上的 Promise 回调都已经执行过了。更多的解释请移步[这里](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)

Another solution is to use an `async` function and the npm package `flush-promises`. `flush-promises` flushes all pending resolved promise handlers. You can `await` the call of `flushPromises` to flush pending promises and improve the readability of your test.
另一个解决方案时使用一个 `async` 函数配合 npm `flush-promises``flush-promises` 会清除所有等待完成的 Promise 具柄。你可以 `await` 该 `flushPromiese` 调用,以此清除等待中的 Promise 并改进你的测试用例的可读性。

The updated test looks like this:

Expand All @@ -94,5 +94,4 @@ test('Foo', () => {
})
```

This same technique can be applied to Vuex actions, which return a promise by default.

相同的技巧可以被运用在同样默认返回一个 Promise 的 Vuex action 中。
1 change: 1 addition & 0 deletions docs/zh-cn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [选择一个测试运行器](guides/choosing-a-test-runner.md)
* [用 Jest 测试单文件组件](guides/testing-SFCs-with-jest.md)
* [用 Mocha 和 webpack 测试单文件组件](guides/testing-SFCs-with-mocha-webpack.md)
* [测试异步行为](guides/testing-async-components.md)
* [配合 Vue Router 使用](guides/using-with-vue-router.md)
* [配合 Vuex 实用](guides/using-with-vuex.md)
* [API](api/README.md)
Expand Down
1 change: 1 addition & 0 deletions docs/zh-cn/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [选择一个测试运行器](guides/choosing-a-test-runner.md)
* [用 Jest 测试单文件组件](guides/testing-SFCs-with-jest.md)
* [用 Mocha 和 webpack 测试单文件组件](guides/testing-SFCs-with-mocha-webpack.md)
* [测试异步行为](guides/testing-async-components.md)
* [配合 Vue Router 使用](guides/using-with-vue-router.md)
 * [配合 Vuex 使用](guides/using-with-vuex.md)
* [API](api/README.md)
Expand Down
12 changes: 10 additions & 2 deletions docs/zh-cn/api/selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

Vue 组件也是有效的选择器。

`vue-test-utils` 使用 `name` 属性搜索匹配 Vue 组件的实例树。

```js
// Foo.vue

Expand All @@ -44,6 +42,16 @@ expect(wrapper.is(Foo)).toBe(true)

## 查找选项对象

### Name

`vue-test-utils` 允许通过一个查找选项对象在组件包裹器上根据 `name` 选择元素。


```js
const buttonWrapper = wrapper.find({ name: 'my-button' })
buttonWrapper.trigger('click')
```

### Ref

`vue-test-utils` 允许通过一个查找选项对象在组件包裹器上根据 `$ref` 选择元素。
Expand Down
1 change: 1 addition & 0 deletions docs/zh-cn/guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
* [选择一个测试运行器](./choosing-a-test-runner.md)
* [用 Jest 测试单文件组件](./testing-SFCs-with-jest.md)
* [用 Mocha 和 webpack 测试单文件组件](./testing-SFCs-with-mocha-webpack.md)
* [测试异步行为](./testing-async-components.md)
* [配合 Vue Router 使用](./using-with-vue-router.md)
* [配合 Vuex 实用](./using-with-vuex.md)
41 changes: 37 additions & 4 deletions docs/zh-cn/guides/testing-SFCs-with-jest.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ npm install --save-dev babel-jest
}
```

### 测试快照
## 测试快照

你可以使用 [`vue-server-renderer`](https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer) 将组件渲染为一个字符串,这样它就可以为 [Jest 快照测试](https://facebook.github.io/jest/docs/en/snapshot-testing.html) 保存一个快照。

Expand All @@ -148,13 +148,46 @@ npm install --save-dev jest-serializer-vue
}
```

### 放置测试文件
## 放置测试文件

默认情况下,Jest 将会递归的找到整个工程里所有 `.spec.js` 或 `.test.js` 扩展名的文件。如果这不符合你的需求,你也可以在 `package.json` 里的配置段落中[改变它的 `testRegex`](https://facebook.github.io/jest/docs/en/configuration.html#testregex-string)。

Jest 推荐你在被测试代码的所在目录下创建一个 `__tests__` 目录,但你也可以为你的测试文件随意设计自己习惯的文件结构。不过要当心 Jest 会为快照测试在临近测试文件的地方创建一个 `__snapshots__` 目录。

### 测试规范示例
## 测试覆盖率

Jest 可以被用来生成多种格式的测试覆盖率报告。以下是一个简单的起步的例子:

扩展你的 `jest` 配置 (通常在 `package.json` 或 `jest.config.js` 中) 的 [`collectCoverage`](https://facebook.github.io/jest/docs/en/configuration.html#collectcoverage-boolean) 选项,然后添加 [`collectCoverageFrom`](https://facebook.github.io/jest/docs/en/configuration.html#collectcoveragefrom-array) 数组来定义需要收集测试覆盖率信息的文件。你还需要设置 [`mapCoverage`](https://facebook.github.io/jest/docs/en/configuration.html#mapcoverage-boolean) 为 `true`,以确保测试覆盖率数据的精准。

```json
{
"jest": {
// ...
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{js,vue}",
"!**/node_modules/**"
],
"mapCoverage": true
}
}
```

这样就会开启[默认格式的测试覆盖率报告](https://facebook.github.io/jest/docs/en/configuration.html#coveragereporters-array-string)。你可以通过 `coverageReporters` 选项来定制它们。

```json
{
"jest": {
// ...
"coverageReporters": ["html", "text-summary"]
}
}
```

更多文档内容请移步至 [Jest 配置文档](https://facebook.github.io/jest/docs/en/configuration.html#collectcoverage-boolean),在那里你可以找到覆盖率阀值、目标输出目录等选项。

## 测试规范示例

如果你已经熟悉了 Jasmine,你应该很适应 Jest 的[断言 API](https://facebook.github.io/jest/docs/en/expect.html#content):

Expand All @@ -170,7 +203,7 @@ describe('Component', () => {
})
```

### 相关资料
## 相关资料

- [该设置的示例工程](https://github.com/vuejs/vue-test-utils-jest-example)
- [Vue Conf 2017 中的示例和幻灯片](https://github.com/codebryo/vue-testing-with-jest-conf17)
Expand Down
4 changes: 4 additions & 0 deletions docs/zh-cn/guides/testing-SFCs-with-mocha-webpack.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ npm run unit

喔,我们的测试运行起来了!

### 测试覆盖率

如果想设置 `mocha-webpack` 的测试覆盖率,请参照 [`mocha-webpack` 测试覆盖率指南](https://github.com/zinserjan/mocha-webpack/blob/master/docs/guides/code-coverage.md)。

### 相关资料

- [该设置的示例工程](https://github.com/vuejs/vue-test-utils-mocha-webpack-example)
Expand Down
97 changes: 97 additions & 0 deletions docs/zh-cn/guides/testing-async-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Testing Asynchronous Behavior

To simplify testing, `vue-test-utils` applies DOM updates _synchronously_. However, there are some techniques you need to be aware of when testing a component with asynchronous behavior such as callbacks or promises.

One of the most common asynchronous behaviors is API calls and Vuex actions. The following examples shows how to test a method that makes an API call. This example uses Jest to run the test and to mock the HTTP library `axios`. More about Jest manual mocks can be found [here](https://facebook.github.io/jest/docs/en/manual-mocks.html#content).

The implementation of the `axios` mock looks like this:

``` js
export default {
get: () => new Promise(resolve => {
resolve({ data: 'value' })
})
}
```

The below component makes an API call when a button is clicked, then assigns the response to `value`.

``` html
<template>
<button @click="fetchResults" />
</template>

<script>
import axios from 'axios'

export default {
data () {
return {
value: null
}
},

methods: {
async fetchResults () {
const response = await axios.get('mock/service')
this.value = response.data
}
}
}
</script>
```

A test can be written like this:

``` js
import { shallow } from 'vue-test-utils'
import Foo from './Foo'
jest.mock('axios')

test('Foo', () => {
it('fetches async when a button is clicked', () => {
const wrapper = shallow(Foo)
wrapper.find('button').trigger('click')
expect(wrapper.vm.value).toEqual('value')
})
})
```

This test currently fails because the assertion is called before the promise in `fetchResults` resolves. Most unit test libraries provide a callback to let the runner know when the test is complete. Jest and Mocha both use `done`. We can use `done` in combination with `$nextTick` or `setTimeout` to ensure any promises resolve before the assertion is made.

``` js
test('Foo', () => {
it('fetches async when a button is clicked', (done) => {
const wrapper = shallow(Foo)
wrapper.find('button').trigger('click')
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.value).toEqual('value')
done()
})
})
})
```

The reason `$nextTick` or `setTimeout` allow the test to pass is because the microtask queue where promise callbacks are processed run before the task queue, where `$nextTick` and `setTimeout` are processed. This means by the time the `$nexTick` and `setTimeout` run, any promise callbacks on the microtask queue will have been executed. See [here](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) for a more detailed explanation.

Another solution is to use an `async` function and the npm package `flush-promises`. `flush-promises` flushes all pending resolved promise handlers. You can `await` the call of `flushPromises` to flush pending promises and improve the readability of your test.

The updated test looks like this:

``` js
import { shallow } from 'vue-test-utils'
import flushPromises from 'flush-promises'
import Foo from './Foo'
jest.mock('axios')

test('Foo', () => {
it('fetches async when a button is clicked', async () => {
const wrapper = shallow(Foo)
wrapper.find('button').trigger('click')
await flushPromises()
expect(wrapper.vm.value).toEqual('value')
})
})
```

This same technique can be applied to Vuex actions, which return a promise by default.
16 changes: 11 additions & 5 deletions docs/zh-cn/guides/using-with-vue-router.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
为了避免这样的事情发生,我们创建了一个 `localVue` 并对其安装 Vue Router。

```js
import { shallow, createLocalVue } from 'vue-test-utils'
import VueRouter from 'vue-router'
const localVue = createLocalVue()

const localVue = createLocalVue()
localVue.use(VueRouter)

shallow(Component, {
Expand All @@ -21,11 +22,13 @@ shallow(Component, {

当你安装 Vue Router 的时候,`router-link` 和 `router-view` 组件就被注册了。这意味着我们无需再导入可以在应用的任意地方使用它们。

当我们运行测试的时候,需要令 vue-router 相关组件在我们挂载的组件中可用。有以下两种做法:
当我们运行测试的时候,需要令 Vue Router 相关组件在我们挂载的组件中可用。有以下两种做法:

### 使用存根

```js
import { shallow } from 'vue-test-utils'

shallow(Component, {
stubs: ['router-link', 'router-view']
})
Expand All @@ -34,9 +37,10 @@ shallow(Component, {
### 为 localVue 安装 Vue Router

```js
import { shallow, createLocalVue } from 'vue-test-utils'
import VueRouter from 'vue-router'
const localVue = createLocalVue()

const localVue = createLocalVue()
localVue.use(VueRouter)

shallow(Component, {
Expand All @@ -49,6 +53,8 @@ shallow(Component, {
有的时候你想要测试一个组件在配合 `$route` 和 `$router` 对象的参数时的行为。这时候你可以传递自定义假数据给 Vue 实例。

```js
import { shallow } from 'vue-test-utils'

const $route = {
path: '/some/path'
}
Expand All @@ -59,7 +65,7 @@ const wrapper = shallow(Component, {
}
})

wrapper.vm.$router // /some/path
wrapper.vm.$router.path // /some/path
```

## 常识
Expand All @@ -68,4 +74,4 @@ wrapper.vm.$router // /some/path

这意味着在未来的任何测试中,伪造 `$route` 或 `$router` 都会失效。

要想回避这个问题,就不要在运行测试的时候安装 Vue Router。
要想回避这个问题,就不要在运行测试的时候全局安装 Vue Router,而用上述的 `localVue` 用法