测试 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;
},
},
});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");
},
},
};const App = {
template: `
<div>
<button @click="increment" />
Count: {{ count }}
</div>
`,
computed: {
count() {
return this.$store.state.count;
},
},
methods: {
increment() {
this.$store.commit("increment");
},
},
};测试真实的vuex store
为了完全测试该组件和Vuex存储是否正常工作,我们将单击<button>并断言计数增加。在你的Vue应用程序中,通常在main.js中,你像这样安装Vuex:
const app = createApp(App);
app.use(store);const app = createApp(App);
app.use(store);这是因为 Vuex 是一个插件。通过调用app.use并传入插件来应用插件。
Vue Test Utils也允许你安装插件,使用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");
});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增加。这种测试涵盖了不同系统之间的交互(在本例中是组件和存储),被称为集成测试。
使用 Mock Store 进行测试
相反,单元测试可以分别隔离和测试组件和存储。如果您有一个具有复杂存储的大型应用程序,这将非常有用。对于这个用例,你可以使用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();
});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.store,并通过global.plugins,安装它。插件中,我们创建了自己的模拟存储,只实现了组件中使用的Vuex部分(在本例中是状态和提交函数)。
虽然单独测试store似乎很方便,但请注意,如果您破坏了Vuex.store,它不会给您任何警告。如果您想模拟Vuex.store,还是使用一个store,请仔细考虑,并了解利弊。以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景
隔离测试 Vuex
您可能希望完全隔离地测试您的Vuex的mutations或actions,特别是在它们很复杂的情况下。你不需要Vue Test Utils,因为Vue store只是普通的JavaScript。下面是在没有Vue test Utils的情况下测试增量突变的方法:
test("increment mutation", () => {
const store = createStore({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count += 1;
},
},
});
store.commit("increment");
expect(store.state.count).toBe(1);
});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存储处于特定的测试状态是很有用的。除了全局,您还可以使用一个有用的技术。mock的目的是创建一个函数,该函数包装createStore并接受一个参数作为初始状态的种子。在本例中,我们扩展了increment来接受一个额外的参数,该参数将被添加到state.count中。如果没有提供,我们就增加state。 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);
});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 Test Utils v1。想法和概念是相同的,Vue测试手册将在不久的将来为Vue.js 3和Vue测试Utils 2更新。
使用组合 API 进行测试
当使用Composition API时,通过useStore函数访问Vuex。点击这里了解更多。
如Vuex文档中所述,useStore可以与一个可选且唯一的注入键一起使用。
它是这样的:
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);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文档建议将该逻辑提取到一个helper函数中,并重用该函数而不是默认的useStore函数。点击这里了解更多。使用Vue Test Utils提供存储的方法取决于组件中使用useStore函数的方式。
测试使用useStore而不是使用注入 Key 的组件
如果没有注入键,存储数据就可以通过全局的提供安装选项注入到组件中。注入的存储库的名称必须与组件中的名称相同。“store”。
提供无键 userstore 的示例
import { createStore } from "vuex";
const store = createStore({
// ...
});
const wrapper = mount(App, {
global: {
provide: {
store: store,
},
},
});import { createStore } from "vuex";
const store = createStore({
// ...
});
const wrapper = mount(App, {
global: {
provide: {
store: store,
},
},
});测试使用带有注入键的useStore的组件
当使用带有注入密钥的存储时,以前的方法将不起作用。存储实例不会从useStore返回。为了访问正确的存储,需要提供标识符。
它需要是在组件的设置函数中传递给useStore或在setup函数中传递到useStore的确切键。由于JavaScript符号是唯一的,无法重新创建,因此最好从实际存储中导出密钥。
您可以使用带有正确密钥的global.provide来注入存储,也可以使用global.plugins来安装存储并指定密钥:
使用 global.provide 提供 Key 以及 useStore
// store.js
export const key = Symbol();// 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,
},
},
});// app.spec.js
import { createStore } from "vuex";
import { key } from "./store";
const store = createStore({
/* ... */
});
const wrapper = mount(App, {
global: {
provide: {
[key]: store,
},
},
});使用 global.plugins 提供 Keyed 以及 useStore
// store.js
export const key = Symbol();// 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]],
},
});// 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的mutations和actions - 用一个函数包装 createStore,该函数使用一个参数来设置特定的测试场景
zerone