diff --git a/docs/zh-cn/README.md b/docs/zh-cn/README.md index f0464bdd2..fe70511e4 100644 --- a/docs/zh-cn/README.md +++ b/docs/zh-cn/README.md @@ -2,6 +2,8 @@ `vue-test-utils` 是 Vue.js 官方的单元测试实用工具库。 +## 目录 + * [教程](guides/README.md) * [起步](guides/getting-started.md) * [常用技巧](guides/common-tips.md) @@ -9,6 +11,7 @@ * [选择一个测试运行器](guides/choosing-a-test-runner.md) * [用 Jest 测试单文件组件](guides/testing-SFCs-with-jest.md) * [用 Mocha 和 webpack 测试单文件组件](guides/testing-SFCs-with-mocha-webpack.md) + * [用 Karma 测试单文件组件](guides/testing-SFCs-with-karma.md) * [测试异步行为](guides/testing-async-components.md) * [配合 Vue Router 使用](guides/using-with-vue-router.md) * [配合 Vuex 实用](guides/using-with-vuex.md) @@ -36,35 +39,34 @@ * [destroy](api/wrapper/destroy.md) * [find](api/wrapper/find.md) * [findAll](api/wrapper/findAll.md) - * [hasStyle](api/wrapper/hasStyle.md) * [html](api/wrapper/html.md) * [is](api/wrapper/is.md) * [isEmpty](api/wrapper/isEmpty.md) * [isVueInstance](api/wrapper/isVueInstance.md) * [name](api/wrapper/name.md) * [props](api/wrapper/props.md) - * [setComputed](api/wrapper/setComputed.md) * [setData](api/wrapper/setData.md) * [setMethods](api/wrapper/setMethods.md) * [setProps](api/wrapper/setProps.md) * [text](api/wrapper/text.md) * [trigger](api/wrapper/trigger.md) * [update](api/wrapper/update.md) + * [visible](api/wrapper/visible.md) * [WrapperArray](api/wrapper-array/README.md) * [at](api/wrapper-array/at.md) * [contains](api/wrapper-array/contains.md) * [exists](api/wrapper/exists.md) * [destroy](api/wrapper-array/destroy.md) - * [hasStyle](api/wrapper-array/hasStyle.md) + * [filter](api/wrapper-array/filter.md) * [is](api/wrapper-array/is.md) * [isEmpty](api/wrapper-array/isEmpty.md) * [isVueInstance](api/wrapper-array/isVueInstance.md) - * [setComputed](api/wrapper-array/setComputed.md) * [setData](api/wrapper-array/setData.md) * [setMethods](api/wrapper-array/setMethods.md) * [setProps](api/wrapper-array/setProps.md) * [trigger](api/wrapper-array/trigger.md) * [update](api/wrapper-array/update.md) + * [visible](api/wrapper-array/visible.md) * [组件](api/components/README.md) * [TransitionStub](api/components/TransitionStub.md) * [TransitionGroupStub](api/components/TransitionGroupStub.md) diff --git a/docs/zh-cn/SUMMARY.md b/docs/zh-cn/SUMMARY.md index ec3661f36..9381d2edc 100644 --- a/docs/zh-cn/SUMMARY.md +++ b/docs/zh-cn/SUMMARY.md @@ -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) + * [用 Karma 测试单文件组件](guides/testing-SFCs-with-karma.md) * [测试异步行为](guides/testing-async-components.md) * [配合 Vue Router 使用](guides/using-with-vue-router.md)  * [配合 Vuex 使用](guides/using-with-vuex.md) @@ -34,7 +35,6 @@ * [destroy](api/wrapper/destroy.md) * [find](api/wrapper/find.md) * [findAll](api/wrapper/findAll.md) - * [hasStyle](api/wrapper/hasStyle.md) * [html](api/wrapper/html.md) * [is](api/wrapper/is.md) * [isEmpty](api/wrapper/isEmpty.md) @@ -47,24 +47,26 @@ * [text](api/wrapper/text.md) * [trigger](api/wrapper/trigger.md) * [update](api/wrapper/update.md) + * [visible](api/wrapper/visible.md) * [WrapperArray](api/wrapper-array/README.md) * [at](api/wrapper-array/at.md) * [contains](api/wrapper-array/contains.md) * [exists](api/wrapper/exists.md) * [destroy](api/wrapper-array/destroy.md) - * [hasStyle](api/wrapper-array/hasStyle.md) + * [filter](api/wrapper-array/filter.md) * [is](api/wrapper-array/is.md) * [isEmpty](api/wrapper-array/isEmpty.md) * [isVueInstance](api/wrapper-array/isVueInstance.md) - * [setComputed](api/wrapper-array/setComputed.md) * [setData](api/wrapper-array/setData.md) * [setMethods](api/wrapper-array/setMethods.md) * [setProps](api/wrapper-array/setProps.md) * [trigger](api/wrapper-array/trigger.md) * [update](api/wrapper-array/update.md) + * [visible](api/wrapper-array/visible.md) * [组件](api/components/README.md) * [TransitionStub](api/components/TransitionStub.md) * [TransitionGroupStub](api/components/TransitionGroupStub.md) + * [RouterLinkStub](api/components/RouterLinkStub.md) * [选择器](api/selectors.md) * [createLocalVue](api/createLocalVue.md) * [配置](api/config.md) diff --git a/docs/zh-cn/api/README.md b/docs/zh-cn/api/README.md index 9bd47ef48..5689a16c9 100644 --- a/docs/zh-cn/api/README.md +++ b/docs/zh-cn/api/README.md @@ -14,6 +14,8 @@ - [provide](./options.md#provide) - [其它选项](./options.md#other-options) * [Wrapper](./wrapper/README.md) + * [attributes](./wrapper/attributes.md) + * [classes](./wrapper/classes.md) * [contains](./wrapper/contains.md) * [emitted](./wrapper/emitted.md) * [emittedByOrder](./wrapper/emittedByOrder.md) @@ -24,35 +26,34 @@ * [hasAttribute](./wrapper/hasAttribute.md) * [hasClass](./wrapper/hasClass.md) * [hasProp](./wrapper/hasProp.md) - * [hasStyle](./wrapper/hasStyle.md) * [html](./wrapper/html.md) * [is](./wrapper/is.md) * [isEmpty](./wrapper/isEmpty.md) * [isVueInstance](./wrapper/isVueInstance.md) * [name](./wrapper/name.md) * [props](./wrapper/props.md) - * [setComputed](./wrapper/setComputed.md) * [setData](./wrapper/setData.md) * [setMethods](./wrapper/setMethods.md) * [setProps](./wrapper/setProps.md) * [text](./wrapper/text.md) * [trigger](./wrapper/trigger.md) * [update](./wrapper/update.md) + * [visible](./wrapper/visible.md) * [WrapperArray](./wrapper-array/README.md) * [at](./wrapper-array/at.md) * [contains](./wrapper-array/contains.md) * [exists](./wrapper/exists.md) * [destroy](./wrapper-array/destroy.md) - * [hasStyle](./wrapper-array/hasStyle.md) + * [filter](./wrapper-array/filter.md) * [is](./wrapper-array/is.md) * [isEmpty](./wrapper-array/isEmpty.md) * [isVueInstance](./wrapper-array/isVueInstance.md) - * [setComputed](./wrapper-array/setComputed.md) * [setData](./wrapper-array/setData.md) * [setMethods](./wrapper-array/setMethods.md) * [setProps](./wrapper-array/setProps.md) * [trigger](./wrapper-array/trigger.md) * [update](./wrapper-array/update.md) + * [visible](./wrapper-array/visible.md) * [组件](./components/README.md) * [TransitionStub](./components/TransitionStub.md) * [TransitionGroupStub](./components/TransitionGroupStub.md) diff --git a/docs/zh-cn/api/components/RouterLinkStub.md b/docs/zh-cn/api/components/RouterLinkStub.md new file mode 100644 index 000000000..b305a76a4 --- /dev/null +++ b/docs/zh-cn/api/components/RouterLinkStub.md @@ -0,0 +1,20 @@ +# `RouterLinkStub` + +一个用来存根 Vue Router 中 `router-link` 组件的组件。 + +你可以在渲染树中使用这个组件查找一个 `router-link` 组件。 + +- **用法:** + +在挂载选项中将其设置为一个存根: + +```js +import { mount, RouterLinkStub } from '@vue/test-utils' + +const wrapper = mount(Component, { + stubs: { + RouterLink: RouterLinkStub + } +}) +expect(wrapper.find(RouterLinkStub).props().to).toBe('/some/path') +``` diff --git a/docs/zh-cn/api/wrapper-array/filter.md b/docs/zh-cn/api/wrapper-array/filter.md new file mode 100644 index 000000000..9237c2828 --- /dev/null +++ b/docs/zh-cn/api/wrapper-array/filter.md @@ -0,0 +1,23 @@ +# `filter(predicate)` + +用一个针对 `Wrapper` 的断言函数过滤 `WrapperArray`。 + +该方法的行为和 [Array.prototype.filter](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) 相同。 + +- **参数:** + - `{function} predicate` + +- **返回值:** `{WrapperArray}` + +一个新的 `WrapperArray` 实例,该实例包含了经过断言函数处理后返回真值的 `Wrapper` 实例。 + +- **示例:** + +```js +import { shallow } from 'vue-test-utils' +import { expect } from 'chai' +import Foo from './Foo.vue' + +const wrapper = shallow(Foo) +const filteredDivArray = wrapper.findAll('div').filter(w => !w.hasClass('filtered')) +``` diff --git a/docs/zh-cn/api/wrapper-array/hasStyle.md b/docs/zh-cn/api/wrapper-array/hasStyle.md deleted file mode 100644 index 69af37a01..000000000 --- a/docs/zh-cn/api/wrapper-array/hasStyle.md +++ /dev/null @@ -1,25 +0,0 @@ -# `hasStyle(style, value)` - -断言 `WrapperArray` 中的每一个 `Wrapper` 的 DOM 节点都有样式的匹配值。 - -如果 `Wrapper` 的 DOM 节点有 `style` 样式值匹配 `value` 则返回 `true`。 - -**注意:当运行在 `jsdom` 中的时候只会匹配内联样式。** - -- **参数:** - - `{string} style` - - `{string} value` - -- **返回值:**`{boolean}` - -- **示例:** - -```js -import { mount } from '@vue/test-utils' -import { expect } from 'chai' -import Foo from './Foo.vue' - -const wrapper = mount(Foo) -const divArray = wrapper.findAll('div') -expect(divArray.hasStyle('color', 'red')).toBe(true) -``` diff --git a/docs/zh-cn/api/wrapper-array/setComputed.md b/docs/zh-cn/api/wrapper-array/setComputed.md deleted file mode 100644 index 2ac0304f3..000000000 --- a/docs/zh-cn/api/wrapper-array/setComputed.md +++ /dev/null @@ -1,25 +0,0 @@ -# `setComputed(computedObjects)` - -为 `WrapperArray` 的每个 `Wrapper` `vm` 都设置计算属性并强行更新。 - -**注意:该包裹器必须包含一个 Vue 示例。** -**注意:每个 Vue 示例必须已经有被传入 `setComputed` 的计算属性。** - -- **参数:** - - `{Object} computed properties` - -- **示例:** - -```js -import { mount } from '@vue/test-utils' -import Foo from './Foo.vue' -import Bar from './Bar.vue' - -const wrapper = mount(Foo) -const barArray = wrapper.findAll(Bar) - -barArray.setComputed({ - computed1: 'new-computed1', - computed2: 'new-computed2' -}) -``` diff --git a/docs/zh-cn/api/wrapper-array/visible.md b/docs/zh-cn/api/wrapper-array/visible.md new file mode 100644 index 000000000..360d5fb25 --- /dev/null +++ b/docs/zh-cn/api/wrapper-array/visible.md @@ -0,0 +1,22 @@ +# `visible()` + +断言每个 `WrapperArray` 中的每个 `Wrapper` 是否可见。 + +如果至少一个元素的祖先拥有 `display: none` 或 `visibility: hidden` 样式则返回 `false`。 + +这可以用于断言一个组件是否被 `v-show` 所隐藏。 + +- **返回值:** `{boolean}` + +- **示例:** + +```js +import { mount } from 'vue-test-utils' +import { expect } from 'chai' +import Foo from './Foo.vue' + +const wrapper = mount(Foo) +expect(wrapper.visible()).toBe(true) +expect(wrapper.findAll('.is-not-visible').visible()).toBe(false) +expect(wrapper.findAll('.is-visible').visible()).toBe(true) +``` diff --git a/docs/zh-cn/api/wrapper/hasStyle.md b/docs/zh-cn/api/wrapper/hasStyle.md deleted file mode 100644 index 543daf3e7..000000000 --- a/docs/zh-cn/api/wrapper/hasStyle.md +++ /dev/null @@ -1,24 +0,0 @@ -# `hasStyle(style, value)` - -断言 `Wrapper` DOM 节点有匹配的样式值。 - -如果 `Wrapper` DOM 节点有 `style` 属性值为 `value` 则返回 `true`。 - -**注意:当运行在 `jsdom` 中时只会检测内联样式。** - -- **参数:** - - `{string} style` - - `{string} value` - -- **返回值:**`{boolean}` - -- **示例:** - -```js -import { mount } from '@vue/test-utils' -import { expect } from 'chai' -import Foo from './Foo.vue' - -const wrapper = mount(Foo) -expect(wrapper.hasStyle('color', 'red')).toBe(true) -``` diff --git a/docs/zh-cn/api/wrapper/visible.md b/docs/zh-cn/api/wrapper/visible.md new file mode 100644 index 000000000..3caffdc21 --- /dev/null +++ b/docs/zh-cn/api/wrapper/visible.md @@ -0,0 +1,21 @@ +# `visible()` + +断言 `Wrapper` 是否可见。 + +如果有一个祖先元素拥有 `display: none` 或 `visibility: hidden` 样式则返回 `false`。 + +这可以用于断言一个组件是否被 `v-show` 所隐藏。 + +- **返回值:** `{boolean}` + +- **示例:** + +```js +import { mount } from 'vue-test-utils' +import { expect } from 'chai' +import Foo from './Foo.vue' + +const wrapper = mount(Foo) +expect(wrapper.visible()).toBe(true) +expect(wrapper.find('.is-not-visible').visible()).toBe(false) +``` diff --git a/docs/zh-cn/guides/README.md b/docs/zh-cn/guides/README.md index 3ab428c5b..27f2478fa 100644 --- a/docs/zh-cn/guides/README.md +++ b/docs/zh-cn/guides/README.md @@ -6,6 +6,7 @@ * [选择一个测试运行器](./choosing-a-test-runner.md) * [用 Jest 测试单文件组件](./testing-SFCs-with-jest.md) * [用 Mocha 和 webpack 测试单文件组件](./testing-SFCs-with-mocha-webpack.md) +* [用 Karma 测试单文件组件](./testing-SFCs-with-karma.md) * [测试异步行为](./testing-async-components.md) * [配合 Vue Router 使用](./using-with-vue-router.md) * [配合 Vuex 实用](./using-with-vuex.md) diff --git a/docs/zh-cn/guides/common-tips.md b/docs/zh-cn/guides/common-tips.md index 69e3ccefd..5b02ac497 100644 --- a/docs/zh-cn/guides/common-tips.md +++ b/docs/zh-cn/guides/common-tips.md @@ -109,7 +109,7 @@ mount(Component, { }) ``` -**注意有些插件会为全局的 Vue 构造函数添加只读属性,比如 Vue Router。这使得我们无法在一个 `localVue` 构造函数上二次安装该插件,或伪造这些属性。** +**注意有些插件会为全局的 Vue 构造函数添加只读属性,比如 Vue Router。这使得我们无法在一个 `localVue` 构造函数上二次安装该插件,或伪造这些只读属性。** ## 仿造注入 @@ -135,3 +135,7 @@ mount(Component, { ## 处理路由 因为路由需要在应用的全局结构中进行定义,且引入了很多组件,所以最好集成到 end-to-end 测试。对于依赖 `vue-router` 功能的独立的组件来说,你可以使用上面提到的技术仿造它们。 + +## 探测样式 + +当你的测试运行在 `jsdom` 中时,可以只探测到内联样式。 diff --git a/docs/zh-cn/guides/testing-SFCs-with-jest.md b/docs/zh-cn/guides/testing-SFCs-with-jest.md index 2d95464ff..38600d48a 100644 --- a/docs/zh-cn/guides/testing-SFCs-with-jest.md +++ b/docs/zh-cn/guides/testing-SFCs-with-jest.md @@ -11,7 +11,7 @@ Jest 是一个由 Facebook 开发的测试运行器,致力于提供一个“be 我们要做的第一件事就是安装 Jest 和 `vue-test-utils`: ```bash -$ npm install --save-dev jest vue-test-utils +$ npm install --save-dev jest @vue/test-utils ``` 然后我们需要在 `package.json` 中定义一个单元测试的脚本。 diff --git a/docs/zh-cn/guides/testing-SFCs-with-karma.md b/docs/zh-cn/guides/testing-SFCs-with-karma.md new file mode 100644 index 000000000..e8232e7bb --- /dev/null +++ b/docs/zh-cn/guides/testing-SFCs-with-karma.md @@ -0,0 +1,202 @@ +# 用 Karma 测试单文件组件 + +> 我们在 [GitHub](https://github.com/eddyerburgh/vue-test-utils-karma-example) 上放有一个该设置的示例工程。 + +Karma 是一个启动浏览器运行测试并生成报告的测试运行器。我们会使用 Mocha 框架撰写测试,同时使用 chai 作为断言库。 + +## 设置 Mocha + +我们会假设你一开始已经正确配置好了 webpack、vue-loader 和 Babel——例如通过 `vue-cli` 的 `webpack-simple` 模板搭建起来。 + +第一件要做的事是安装测试依赖: + +``` bash +npm install --save-dev @vue/test-utils karma karma-chrome-launcher karma-mocha karma-sourcemap-loader karma-spec-reporter karma-webpack mocha +``` + +接下来我们需要在 `package.json` 定义一个测试脚本。 + +```json +// package.json +{ + "scripts": { + "test": "karma start --single-run" + } +} +``` + +- `--single-run` 标识告诉了 Karma 一次性运行该测试套件。 + +### Karma 配置 + +在项目的主目录创建一个 `karma.conf.js` 文件: + +```js +// karma.conf.js + +var webpackConfig = require('./webpack.config.js') + +module.exports = function (config) { + config.set({ + frameworks: ['mocha'], + + files: [ + 'test/**/*.spec.js' + ], + + preprocessors: { + '**/*.spec.js': ['webpack', 'sourcemap'] + }, + + webpack: webpackConfig, + + reporters: ['spec'], + + browsers: ['Chrome'] + }) +} +``` + +这个文件用来配置 Karma。 + +我们需要用 webpack 预处理文件。为此,我们将 webpack 添加为预处理器,并引入我们的 webpack 配置。我们可以在项目基础中使用该 webpack 配置文件而无需任何修改。 + +在我们的配置中,我们在 Chrome 中运行测试。如果想添加其它浏览器,可查阅[Karma 文档的浏览器章节](http://karma-runner.github.io/2.0/config/browsers.html)。 + +### 选用一个断言库 + +[Chai](http://chaijs.com/) 是一个流行的常配合 Mocha 使用的断言库。你也可以选用 [Sinon](http://sinonjs.org/) 来创建监视和存根。 + +我们可以安装 `karma-chai` 插件以在我们的测试中使用 `chai`。 + +``` bash +npm install --save-dev karma-chai +``` + +### 添加一个测试 + +在 `src` 中创建一个名为 `Counter.vue` 的文件: + +``` html + + + +``` + +然后添加一个名为 `test/Coutner.spec.js` 的测试文件,并写入如下代码: + +```js +import { expect } from 'chai' +import { shallow } from '@vue/test-utils' +import Counter from '../src/Counter.vue' + +describe('Counter.vue', () => { + it('increments count when button is clicked', () => { + const wrapper = shallow(Counter) + wrapper.find('button').trigger('click') + expect(wrapper.find('div').text()).contains('1') + }) +}) +``` + +接下来我们可以运行测试: + +``` +npm run test +``` + +Woohoo,我们的测试跑起来了! + +### 覆盖率 + +我们可以使用 `karma-coverage` 插件来设置 Karma 的代码覆盖率。 + +默认情况下,`karma-coverage` 不会使用 source map 来对照覆盖率报告。所以我们需要使用 `babel-plugin-istanbul` 来确认正确匹配的覆盖率。 + +安装 `karma-coverage`、`babel-plugin-istanbul` 和 `cross-env`: + +``` +npm install --save-dev karma-coverage cross-env +``` + +我们会使用 `cross-env` 来设置一个 `BABEL_ENV` 环境变量。这样我们就可以在编译测试的时候使用 `babel-plugin-istanbul`——因为我们不想在生产环境下引入 `babel-plugin-istnabul`: + +``` +npm install --save-dev babel-plugin-istanbul +``` + +更新你的 `.babelrc` 文件,在因测试设置了 `BABEL_ENV` 时使用 `babel-plugin-istanbul`: + +```json +{ + "presets": [ + ["env", { "modules": false }], + "stage-3" + ], + "env": { + "test": { + "plugins": ["istanbul"] + } + } +} +``` + +现在更新 `karma.conf.js` 文件来进行覆盖率测试。添加 `coverage` 到 `reporters` 数组,并添加一个 `coverageReporter` 字段: + +```js +// karma.conf.js + +module.exports = function (config) { + config.set({ + // ... + + reporters: ['spec', 'coverage'], + + coverageReporter: { + dir: './coverage', + reporters: [ + { type: 'lcov', subdir: '.' }, + { type: 'text-summary' } + ] + } + }) +} +``` + +然后更新 `test` 脚本来设置 `BABEL_ENV`: + +```json +// package.json +{ + "scripts": { + "test": "cross-env BABEL_ENV=test karma start --single-run" + } +} +``` + +### 相关资料 + +- [该设置的示例工程](https://github.com/eddyerburgh/vue-test-utils-karma-example) +- [Karma](http://karma-runner.github.io/) +- [Mocha](https://mochajs.org/) +- [Chai](http://chaijs.com/) +- [Sinon](http://sinonjs.org/) diff --git a/docs/zh-cn/guides/testing-SFCs-with-mocha-webpack.md b/docs/zh-cn/guides/testing-SFCs-with-mocha-webpack.md index 67fbe5be3..e12062125 100644 --- a/docs/zh-cn/guides/testing-SFCs-with-mocha-webpack.md +++ b/docs/zh-cn/guides/testing-SFCs-with-mocha-webpack.md @@ -165,7 +165,7 @@ describe('Counter.vue', () => { 现在我们运行测试: ``` -npm run unit +npm run test ``` 喔,我们的测试运行起来了! diff --git a/docs/zh-cn/guides/using-with-vuex.md b/docs/zh-cn/guides/using-with-vuex.md index 05daa1973..d7c59e665 100644 --- a/docs/zh-cn/guides/using-with-vuex.md +++ b/docs/zh-cn/guides/using-with-vuex.md @@ -1,8 +1,10 @@ # 配合 Vuex 使用 -在本教程中,我们将会看到如何用 `vue-test-utils` 测试组件中的 Vuex。 +在本教程中,我们将会看到如何用 `vue-test-utils` 测试组件中的 Vuex,以及如何测试一个 Vuex store。 -## 伪造 Action +## 在组件中测试 Vuex + +### 伪造 Action 我们来看一些代码。 @@ -259,9 +261,132 @@ describe('Modules.vue', () => { }) ``` +## 测试一个 Vuex Store + +这里有两个测试 Vuex store 的方式。第一个方式是分别单元化测试 getter、mutation 和 action。第二个方式是创建一个 store 并针对其进行测试。我们接下来看看这两种方式如何。 + +为了弄清楚如果测试一个 Vuex store,我们会创建一个简单的计数器 store。该 store 会有一个 `increment` mutation 和一个 `counter` getter。 + +```js +// mutations.js +export default { + increment (state) { + state.count++ + } +} + +``` + +```js +// getters.js +export default { + evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd' +} +``` + +### 分别测试 getter、mutation 和 action + +Getter、mutation 和 action 全部是 JavaScript 函数,所以我们可以不通过 `vue-test-utils` 或 Vuex 测试它们。 + +分别测试 getter、mutation 和 action 的好处是你的单元测试是非常详细的。当它们失败时,你完全知道你代码的问题是什么。当然另外一方面你需要伪造诸如 `commit` 和 `dispatch` 的 Vuex 函数。这会导致在一些情况下你伪造错了东西,导致单元测试通过,生产环境的代码缺失败了。 + +我们会创建两个测试文件:`mutations.spec.js` 和 `getters.spec.js`: + +首先,我们测试名为 `increment` 的 mutation: + +```js +// mutations.spec.js + +import mutations from './mutations' + +test('increment increments state.count by 1', () => { + const state = { + count: 0 + } + mutations.increment(state) + expect(state.count).toBe(1) +}) +``` + +现在让我们测试 `evenOrOdd` getter。我们可以通过创建一个伪造的 `state` 来测试它,带上 `state` 调用这个 getter 并检查它是否返回正确的结果。 + +```js +// getters.spec.js + +import getters from './getters' + +test('evenOrOdd returns even if state.count is even', () => { + const state = { + count: 2 + } + expect(getters.evenOrOdd(state)).toBe('even') +}) + +test('evenOrOdd returns odd if state.count is even', () => { + const state = { + count: 1 + } + expect(getters.evenOrOdd(state)).toBe('odd') +}) + +``` + +### 测试一个运行中的 store + +另一个测试 Vuex store 的方式就是使用 store 配置创建一个运行中的 store。 + +这样做的好处是我们不需要伪造任何 Vuex 函数。 + +另一方面当一个测试失败时,排查问题的难度会增加。 + +我们来写一个测试吧。当我们创建一个 store 时,我们会使用 `localVue` 来避免污染 Vue 的基础构造函数。该测试会使用 `store-config.js` 导出的配置创建一个 store: + +```js +import mutations from './mutations' +import getters from './getters' + +export default { + state: { + count: 0 + }, + mutations, + getters +} +``` + +```js +// store-config.spec.js + +import { createLocalVue } from '@vue/test-utils' +import Vuex from 'vuex' +import storeConfig from './store-config' +import { cloneDeep } from 'lodash' + +test('increments count value when increment is commited', () => { + const localVue = createLocalVue() + localVue.use(Vuex) + const store = new Vuex.Store(cloneDeep(storeConfig)) + expect(store.state.count).toBe(0) + store.commit('increment') + expect(store.state.count).toBe(1) +}) + +test('updates evenOrOdd getter when increment is commited', () => { + const localVue = createLocalVue() + localVue.use(Vuex) + const store = new Vuex.Store(cloneDeep(storeConfig)) + expect(store.getters.evenOrOdd).toBe('even') + store.commit('increment') + expect(store.getters.evenOrOdd).toBe('odd') +}) +``` + +注意我们在创建一个 store 之前使用了 `cloneDeep` 来克隆 store 配置。这是因为 Vuex 会改变用来创建 store 的选项对象。为了确保我们能为每一个测试都提供一个干净的 store,我们需要克隆 `storeConfig` 对象。 + ### 相关资料 -- [该设置的示例工程](https://github.com/eddyerburgh/vue-test-utils-vuex-example) +- [测试组件的示例工程](https://github.com/eddyerburgh/vue-test-utils-vuex-example) +- [测试 store 的示例工程](https://github.com/eddyerburgh/testing-vuex-store-example) - [`localVue`](../api/options.md#localvue) - [`createLocalVue`](../api/createLocalVue.md)