测试 Vuex
Vuex 只是一个实现细节;测试使用 Vuex 的组件不需要特殊处理。也就是说,有一些技巧可以使您的测试更容易阅读和编写。我们将在本文中介绍这些技巧。
本指南假设您熟悉 Vuex。Vuex 4 是与 Vue.js 3 一起使用的版本。阅读文档 此处。
一个简单的例子
这是一个简单的 Vuex 商店,以及一个依赖于 Vuex 商店存在的组件
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
increment(state: any) {
state.count += 1
}
}
})该商店只存储一个计数,当提交 increment 变异时,它会增加计数。这是我们将要测试的组件
const App = {
template: `
<div>
<button @click="increment" />
Count: {{ count }}
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
methods: {
increment() {
this.$store.commit('increment')
}
}
}使用真实的 Vuex 商店进行测试
为了完全测试该组件和 Vuex 商店是否正常工作,我们将点击 <button> 并断言计数已增加。在您的 Vue 应用程序中,通常在 main.js 中,您会像这样安装 Vuex
const app = createApp(App)
app.use(store)这是因为 Vuex 是一个插件。插件通过调用 app.use 并传入插件来应用。
Vue 测试工具允许您也安装插件,使用 global.plugins 挂载选项。
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
increment(state: any) {
state.count += 1
}
}
})
test('vuex', async () => {
const wrapper = mount(App, {
global: {
plugins: [store]
}
})
await wrapper.find('button').trigger('click')
expect(wrapper.html()).toContain('Count: 1')
})安装插件后,我们使用 trigger 点击按钮并断言 count 已增加。这种涵盖不同系统(在本例中为组件和商店)之间交互的测试被称为集成测试。
使用模拟商店进行测试
相反,单元测试可能会隔离并分别测试组件和商店。如果您有一个具有复杂商店的非常大的应用程序,这可能很有用。对于这种情况,您可以使用 global.mocks 模拟您感兴趣的商店部分
test('vuex using a mock store', async () => {
const $store = {
state: {
count: 25
},
commit: jest.fn()
}
const wrapper = mount(App, {
global: {
mocks: {
$store
}
}
})
expect(wrapper.html()).toContain('Count: 25')
await wrapper.find('button').trigger('click')
expect($store.commit).toHaveBeenCalled()
})我们没有使用真实的 Vuex 商店并通过 global.plugins 安装它,而是创建了自己的模拟商店,只实现了组件中使用的 Vuex 部分(在本例中为 state 和 commit 函数)。
虽然单独测试商店可能看起来很方便,但请注意,如果您破坏了 Vuex 商店,它不会向您发出任何警告。仔细考虑您是否要模拟 Vuex 商店,或者使用真实的商店,并了解权衡取舍。
隔离测试 Vuex
您可能希望完全隔离地测试 Vuex 变异或操作,尤其是当它们很复杂时。您不需要 Vue 测试工具来执行此操作,因为 Vuex 商店只是普通的 JavaScript。以下是如何在没有 Vue 测试工具的情况下测试 increment 变异
test('increment mutation', () => {
const store = createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count += 1
}
}
})
store.commit('increment')
expect(store.state.count).toBe(1)
})预设 Vuex 状态
有时,让 Vuex 商店处于特定状态以进行测试可能很有用。除了 global.mocks 之外,您可以使用的一种有用技术是创建一个包装 createStore 的函数,并接受一个参数来播种初始状态。在本例中,我们扩展了 increment 以接受一个额外的参数,该参数将添加到 state.count 中。如果没有提供,我们只将 state.count 增加 1。
const createVuexStore = (initialState) =>
createStore({
state: {
count: 0,
...initialState
},
mutations: {
increment(state, value = 1) {
state.count += value
}
}
})
test('increment mutation without passing a value', () => {
const store = createVuexStore({ count: 20 })
store.commit('increment')
expect(store.state.count).toBe(21)
})
test('increment mutation with a value', () => {
const store = createVuexStore({ count: -10 })
store.commit('increment', 15)
expect(store.state.count).toBe(5)
})通过创建一个接受初始状态的 createVuexStore 函数,我们可以轻松地设置初始状态。这使我们能够测试所有边缘情况,同时简化我们的测试。
该 Vue 测试手册 有更多关于测试 Vuex 的示例。注意:这些示例适用于 Vue.js 2 和 Vue 测试工具 v1。想法和概念是相同的,Vue 测试手册将在不久的将来更新为 Vue.js 3 和 Vue 测试工具 2。
使用组合 API 进行测试
使用组合 API 时,可以通过 useStore 函数访问 Vuex。 在此处阅读更多信息。
useStore 可以与可选且唯一的注入键一起使用,如所讨论的 Vuex 文档中。
它看起来像这样
import { createStore } from 'vuex'
import { createApp } from 'vue'
// create a globally unique symbol for the injection key
const key = Symbol()
const App = {
setup () {
// use unique key to access store
const store = useStore(key)
}
}
const store = createStore({ /* ... */ })
const app = createApp({ /* ... */ })
// specify key as second argument when calling app.use(store)
app.use(store, key)为了避免每次使用 useStore 时重复键参数传递,Vuex 文档建议将该逻辑提取到一个辅助函数中,并重用该函数而不是默认的 useStore 函数。 在此处阅读更多信息。使用 Vue 测试工具提供商店的方法取决于在组件中使用 useStore 函数的方式。
测试使用 useStore 但没有注入键的组件
如果没有注入键,商店数据可以通过全局 provide 挂载选项注入到组件中。注入商店的名称必须与组件中的名称相同,例如“store”。
提供未键入的 useStore 的示例
import { createStore } from 'vuex'
const store = createStore({
// ...
})
const wrapper = mount(App, {
global: {
provide: {
store: store
},
},
})测试使用 useStore 且具有注入键的组件
当使用具有注入键的商店时,之前的方法将不起作用。商店实例不会从 useStore 返回。为了访问正确的商店,需要提供标识符。
它必须是传递给组件 setup 函数中的 useStore 或自定义辅助函数中的 useStore 的确切键。由于 JavaScript 符号是唯一的,无法重新创建,因此最好从真实商店导出键。
您可以使用 global.provide 提供正确的键来注入商店,或者使用 global.plugins 安装商店并指定键
使用 global.provide 提供键入的 useStore
// store.js
export const key = Symbol()// app.spec.js
import { createStore } from 'vuex'
import { key } from './store'
const store = createStore({ /* ... */ })
const wrapper = mount(App, {
global: {
provide: {
[key]: store
},
},
})使用 global.plugins 提供键入的 useStore
// store.js
export const key = Symbol()// app.spec.js
import { createStore } from 'vuex'
import { key } from './store'
const store = createStore({ /* ... */ })
const wrapper = mount(App, {
global: {
// to pass options to plugins, use the array syntax.
plugins: [[store, key]]
},
})结论
- 使用
global.plugins将 Vuex 安装为插件 - 使用
global.mocks模拟全局对象(如 Vuex)以用于高级用例 - 考虑隔离测试复杂的 Vuex 变异和操作
- 使用接受参数来设置特定测试场景的函数包装
createStore