运行单元测试时,Vue Pinia 函数在 onMounted 中未定义

Jos*_*nay 5 javascript unit-testing vue.js pinia vitest

我有一个组件和一个 Pinia 存储,其中包含状态和一些操作。该代码在浏览器和 E2E(cypress)测试中运行得非常好,但在单元测试中失败。我正在使用 vue-testing-utils 和 vitest。

\n

单击按钮时,可以从单元测试中很好地调用存储函数,但是如果该函数位于已安装的脚本或主脚本中,则测试将失败

\n

src/components/UsersComponent.vue

\n
<script setup>\nimport { onMounted } from 'vue'\nimport { useUsersStore } from '@/stores/users.store'\n\nconst usersStore = useUsersStore()\n// usersStore.resetStatus() // <- This fails in the unit test\n\nonMounted(() => {\n  usersStore.resetStatus() // <- This fails in the unit test\n})\n\nfunction changeStatus() {\n  usersStore.changeStatus() // <- This passes in the unit test\n}\n</script>\n\n<template>\n  <div>\n    <p>Status: {{ usersStore.status }}</p>\n    <button @click="changeStatus()">Change Status</button>\n  </div>\n</template>\n
Run Code Online (Sandbox Code Playgroud)\n

src/stores/users.store.js

\n
import { defineStore } from 'pinia'\nimport { usersAPI } from '@/gateways'\n\nexport const useUsersStore  = defineStore({\n  id: 'users',\n  persist: true,\n\n  state: () => ({\n    status: 'ready',\n  }),\n\n  getters: {},\n\n  actions: {\n    resetStatus() {\n      this.status = 'ready'\n    },\n    changeStatus() {\n      this.status = 'loading'\n    },\n  },\n})\n
Run Code Online (Sandbox Code Playgroud)\n

src/组件/测试/UsersComponent.spec.js

\n
import { describe, it, expect, vi, beforeEach } from 'vitest'\nimport { mount } from '@vue/test-utils'\nimport { createTestingPinia } from '@pinia/testing'\n\nimport UsersComponent from '@/components/UsersComponent.vue'\nimport { useUsersStore } from '@/stores/users.store'\n\nconst wrapper = mount(UsersComponent, {\n  global: {\n    plugins: [createTestingPinia({ createSpy: vi.fn() })],\n  },\n})\nconst usersStore = useUsersStore()\n\ndescribe('UsersComponent', () => {\n  it('store function is called', async () => {\n    // arrange\n    const spy = vi.spyOn(usersStore, 'resetStatus')\n    const button = wrapper.find('button')\n\n    // act\n    await button.trigger('click')\n\n    // assert\n    expect(spy).toHaveBeenCalled()\n  })\n})\n
Run Code Online (Sandbox Code Playgroud)\n

单元测试返回 2 个不同的错误。第一个是函数尝试运行时的控制台日志onMounted(),第二个是 vitest 返回的内容。

\n
stderr | unknown test\n[Vue warn]: Unhandled error during execution of mounted hook \n  at <UsersComponent ref="VTU_COMPONENT" >\n  at <VTUROOT>\n
Run Code Online (Sandbox Code Playgroud)\n
 FAIL  src/components/__tests__/UsersComponent.spec.js [ src/components/__tests__/UsersComponent.spec.js ]\nTypeError: usersStore.resetStatus is not a function\n \xe2\x9d\xaf src/components/UsersComponent.vue:16:14\n     16|\n     17| <template>\n     18|   <div>\n       |  ^\n     19|     <p>Status: {{ usersStore.status }}</p>\n     20|     <button @click="changeStatus()">Change Status</button>\n
Run Code Online (Sandbox Code Playgroud)\n

我知道这个例子有点基础,并没有真正达到目的,但我想知道如何在onMounted()(或类似的地方)内拥有存储功能,而不破坏我的所有单元测试。

\n

小智 0

也许这对你有用:

describe('UsersComponent',  () => {
  it('changeStatus function is called', async () => {
    const wrapper = mount(UsersComponent, {
      mounted: vi.fn(), // With this you mock the onMounted
      global: {
        plugins: [createTestingPinia({
          initialState: { // Initialize the state
            users: { status: 'ready' }, 
          }
        })]
      }
    })  
  
    // Spy the method you call...
    const spy = vi.spyOn(wrapper.vm, 'changeStatus');

    wrapper.vm.changeStatus()

    expect(spy).toHaveBeenCalled()
    expect(spy).toHaveBeenCalledTimes(1)
  })
})
Run Code Online (Sandbox Code Playgroud)