测试组件内的 Vuex — state 和 getters

本页中描述的测试源码可以在 这里组件中的 Vuex - $state 和 getters - 图1 找到。

使用 createLocalVue 测试 $store.state

在一个普通的 Vue 应用中,我们使用 Vue.use(Vuex) 来安装 Vuex 插件,并将一个新的 Vuex store 传入 app 中。如果我们也在一个单元测试中做同样的事,那么,所有单元测试都得接收那个 Vuex store,尽管测试中根本用不到它。vue-test-utils 提供了一个 createLocalVue 方法,用来为测试提供一个临时 Vue 实例。让我们看看如何使用它。首先,是一个基于 store 的 state 渲染出一个 username 的 <ComponentWithGetters> 组件。

  1. <template>
  2. <div>
  3. <div class="username">
  4. {{ username }}
  5. </div>
  6. </div>
  7. </template>
  8. <script>
  9. export default {
  10. name: "ComponentWithVuex",
  11. data() {
  12. return {
  13. username: this.$store.state.username
  14. }
  15. }
  16. }
  17. </script>

我们可以使用 createLocalVue 创建一个临时的 Vue 实例,并用其安装 Vuex。而后我们将一个新的 store 传入组件的加载选项中。完整的测试看起来是这样的:

  1. import Vuex from "vuex"
  2. import { shallowMount, createLocalVue } from "@vue/test-utils"
  3. import ComponentWithVuex from "@/components/ComponentWithVuex.vue"
  4. const localVue = createLocalVue()
  5. localVue.use(Vuex)
  6. const store = new Vuex.Store({
  7. state: {
  8. username: "alice"
  9. }
  10. })
  11. describe("ComponentWithVuex", () => {
  12. it("renders a username using a real Vuex store", () => {
  13. const wrapper = shallowMount(ComponentWithVuex, {
  14. store,
  15. localVue
  16. })
  17. expect(wrapper.find(".username").text()).toBe("alice")
  18. })
  19. })

测试通过。创建一个新的 localVue 实例引入了一些样板文件(boilerplate),并且测试也很长。如果你有好多使用了 Vuex store 的组件要测试,一个替代方法是使用 mocks 加载选项,用以简化 store 的 mock。

使用一个 mock 的 store

通过使用 mocks 加载选项,可以 mock 掉全局的 $store 对象。这意味着你不需要使用 createLocalVue,或创建一个新的 Vuex store 了。使用此项技术,以上测试可以重写成这样:

  1. it("renders a username using a mock store", () => {
  2. const wrapper = shallowMount(ComponentWithVuex, {
  3. mocks: {
  4. $store: {
  5. state: { username: "alice" }
  6. }
  7. }
  8. })
  9. expect(wrapper.find(".username").text()).toBe("alice")
  10. })

我个人更喜欢这种实现。所有必须的数据被声明在测试内部,同时它也更紧凑一点儿。当然两种技术都很有用,并没有哪种更好哪种更差之分。

测试 getters

使用上述技术,getters 同样易于测试。首先,是用于测试的组件:

  1. <template>
  2. <div class="fullname">
  3. {{ fullname }}
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: "ComponentWithGetters",
  9. computed: {
  10. fullname() {
  11. return this.$store.getters.fullname
  12. }
  13. }
  14. }
  15. </script>

我们想要断言组件正确地渲染了用户的 fullname。对于该测试,我们不关心 fullname 来自何方,组件渲染正常就行。

先看看用真实的 Vuex store 和 createLocalVue,测试看起来是这样的:

  1. const localVue = createLocalVue()
  2. localVue.use(Vuex)
  3. const store = new Vuex.Store({
  4. state: {
  5. firstName: "Alice",
  6. lastName: "Doe"
  7. },
  8. getters: {
  9. fullname: (state) => state.firstName + " " + state.lastName
  10. }
  11. })
  12. it("renders a username using a real Vuex getter", () => {
  13. const wrapper = shallowMount(ComponentWithGetters, { store, localVue })
  14. expect(wrapper.find(".fullname").text()).toBe("Alice Doe")
  15. })

测试很紧凑 — 只有两行代码。不过也引入了很多设置代码 — 我们基本上重建了 Vuex store。一个替代方法是引入有着真正 getters 的真实的 Vuex store。这将引入测试中的另一项依赖,当开发一个大系统时,Vuex store 可能由另一位程序员开发,也可能尚未实现。

让我看看使用 mocks 加载选项编写测试的情况:

  1. it("renders a username using computed mounting options", () => {
  2. const wrapper = shallowMount(ComponentWithGetters, {
  3. mocks: {
  4. $store: {
  5. getters: {
  6. fullname: "Alice Doe"
  7. }
  8. }
  9. }
  10. })
  11. expect(wrapper.find(".fullname").text()).toBe("Alice Doe")
  12. })

现在全部所需的数据都包含在测试中了。太棒了!我特喜欢这个,因为测试是全包含的(fully contained),理解组件应该做什么所需的所有知识都都包含在测试中。

使用 computed 加载选项,我们甚至能让测试变得更简单。

computed 来模拟 getters

getters 通常被包裹在 computed 属性中。请记住,这个测试就是为了在给定 store 中的当前 state 时,确保组件行为的正确性。我们不测试 fullname 的实现或是要瞧瞧 getters 是否工作。这意味着我们可以简单地替换掉真实 store,或使用 computed 加载选项 mock 掉 store。测试可以重写为:

  1. it("renders a username using computed mounting options", () => {
  2. const wrapper = shallowMount(ComponentWithGetters, {
  3. computed: {
  4. fullname: () => "Alice Doe"
  5. }
  6. })
  7. expect(wrapper.find(".fullname").text()).toBe("Alice Doe")
  8. })

这比之前两个测试更简洁了,并且仍然表达了组件的意图。

mapStatemapGetters 辅助选项

上述技术都能与 Vuex 的 mapStatemapGetters 辅助选项结合起来工作。我们可以将 ComponentWithGetters 更新为:

  1. import { mapGetters } from "vuex"
  2. export default {
  3. name: "ComponentWithGetters",
  4. computed: {
  5. ...mapGetters([
  6. 'fullname'
  7. ])
  8. }
  9. }

测试仍然通过。

总结

本文讨论了:

  • 使用 createLocalVue 和真实 Vuex store 测试 $store.stategetters
  • 使用 mocks 加载选项 mock 掉 $store.stategetters
  • 使用 computed 加载选项以设置 Vuex getter 的期望值

单独地测试 Vuex getters 实现的技术可以在 这篇向导组件中的 Vuex - $state 和 getters - 图2 中找到。

本页中描述的测试源码可以在 这里组件中的 Vuex - $state 和 getters - 图3 找到。