开发者问题收集

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

2022-04-05
6123

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

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

src/components/UsersComponent.vue

<script setup>
import { onMounted } from 'vue'
import { useUsersStore } from '@/stores/users.store'

const usersStore = useUsersStore()
// usersStore.resetStatus() // <- This fails in the unit test

onMounted(() => {
  usersStore.resetStatus() // <- This fails in the unit test
})

function changeStatus() {
  usersStore.changeStatus() // <- This passes in the unit test
}
</script>

<template>
  <div>
    <p>Status: {{ usersStore.status }}</p>
    <button @click="changeStatus()">Change Status</button>
  </div>
</template>

src/stores/users.store.js

import { defineStore } from 'pinia'
import { usersAPI } from '@/gateways'

export const useUsersStore  = defineStore({
  id: 'users',
  persist: true,

  state: () => ({
    status: 'ready',
  }),

  getters: {},

  actions: {
    resetStatus() {
      this.status = 'ready'
    },
    changeStatus() {
      this.status = 'loading'
    },
  },
})

src/components/ tests /UsersComponent.spec.js

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { createTestingPinia } from '@pinia/testing'

import UsersComponent from '@/components/UsersComponent.vue'
import { useUsersStore } from '@/stores/users.store'

const wrapper = mount(UsersComponent, {
  global: {
    plugins: [createTestingPinia({ createSpy: vi.fn() })],
  },
})
const usersStore = useUsersStore()

describe('UsersComponent', () => {
  it('store function is called', async () => {
    // arrange
    const spy = vi.spyOn(usersStore, 'resetStatus')
    const button = wrapper.find('button')

    // act
    await button.trigger('click')

    // assert
    expect(spy).toHaveBeenCalled()
  })
})

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

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

我知道这个例子有点基础并且没有什么用处,但是我想知道如何在 onMounted() (或类似位置)内拥有存储函数而不会破坏我所有的单元测试。

2个回答

也许这对你有用:

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)
  })
})
Ernesto José Jiménez Canquiz
2022-06-05

我使用 vitest、pinia 和 testing-library(基于 vue-test-utils)。 恕我直言,没有必要:

  • 模拟 onMounted:您的目标不是检查 onMounted 是否被调用 => 这是 Vue 内部测试

  • 使用 vi.spyOn,默认情况下,pinia 会模拟所有方法。您可以在测试中直接执行此操作:

    const usersStore = useUsersStore();

    expect(usersStore.resetStatus).toHaveBeenCalledTimes(1);

我对所有从商店调用 http 调用的 onBeforeMount、onMounted 都执行此操作

onMounted(async() => {
  await Promise.all([
    valuesStore.getAllValues(),
    serviceRequestStore.getServiceRequest(),
  ]);
});

并在测试中

it('gets the service request from the serviceRequest store', () => {
  renderTheComponent();

  const serviceRequestStore = useServiceRequestStore();
  expect(serviceRequestStore.getServiceRequest).toHaveBeenCalledTimes(1);
});

我认为这应该以相同的方式与 vue-test-utils 一起工作。 我的所有组件钩子都是异步的并返回承诺。

John Degey
2023-04-19