Skip to content

测试 vuex

Vuex只是一个实现细节;使用Vuex测试组件不需要特殊处理。也就是说,有一些技术可以使您的测试更容易阅读和编写。我们来看看这些。

本指南假设您熟悉VuexVuex 4是与Vue.js 3一起工作的版本。阅读这里的文档。

简单的例子

下面是一个简单的 Vuex 存储,以及一个依赖于 Vuex 存储的组件:

js
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增量变化时增加该计数。这是我们将要测试的组件:

js
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:

js
const app = createApp(App);
app.use(store);
const app = createApp(App);
app.use(store);

这是因为 Vuex 是一个插件。通过调用app.use并传入插件来应用插件。

Vue Test Utils也允许你安装插件,使用global.plugins安装选项。

js
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模拟你感兴趣的存储部分:

js
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

您可能希望完全隔离地测试您的Vuexmutationsactions,特别是在它们很复杂的情况下。你不需要Vue Test Utils,因为Vue store只是普通的JavaScript。下面是在没有Vue test Utils的情况下测试增量突变的方法:

js
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中。如果没有提供,我们就增加statestate.count 增 1.

js
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 2Vue Test Utils v1。想法和概念是相同的,Vue测试手册将在不久的将来为Vue.js 3Vue测试Utils 2更新。

使用组合 API 进行测试

当使用Composition API时,通过useStore函数访问Vuex。点击这里了解更多。

Vuex文档中所述,useStore可以与一个可选且唯一的注入键一起使用。

它是这样的:

js
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 的示例

js
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

js
// store.js
export const key = Symbol();
// store.js
export const key = Symbol();
js
// 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

js
// store.js
export const key = Symbol();
// store.js
export const key = Symbol();
js
// 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.pluginsVuex作为插件安装
  • 使用global.mocks模拟全局对象,如Vuex,用于高级用例
  • 考虑单独测试复杂的Vuexmutationsactions
  • 用一个函数包装 createStore,该函数使用一个参数来设置特定的测试场景