vue pinia函数在运行单位测试时未定义。

发布于 2025-01-19 02:33:32 字数 2898 浏览 1 评论 0原文

我有一个组件和一个PINIA商店,其中包含一个状态和一些动作。该代码在浏览器和E2E(柏树)测试中的工作状态非常好,但在单位测试中失败。我正在使用Vue测试效果和vitest。

单击按钮时,可以从单元测试中调用商店功能,但是如果该函数在已安装或主脚本中,则会使测试失败

src/components/userscomponent.vue.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/测试/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()(或类似的位置)中拥有存储功能而不会破坏我所有单位测试。

I have a component and a Pinia store which contains a state and some actions. The code works perfectly fine in browser and in E2E (cypress) tests, but fail on unit tests. I'm using vue-testing-utils and vitest.

The store function can be called fine from the unit test when the button is clicked, but if the function is in the mounted or main script, it fails the test

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()
  })
})

The unit tests return 2 different error. The first is a console log when the function tries to run in onMounted() and the second is what vitest returns.

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>

I know this example is a little basic and doesn't really serve a purpose, but I'm wondering how I can have store functions inside the onMounted() (or similar places) without it breaking all my unit tests.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

弥繁 2025-01-26 02:33:32

也许这对你有用:

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)
  })
})

Maybe this can be useful to you:

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)
  })
})
得不到的就毁灭 2025-01-26 02:33:32

我使用vitest,pinia和测试图(基于VUE检验)。
恕我直言,没有必要:

  • 要模拟onmounted:您的目标不是要检查on已登机称为=&gt;这是VUE内部测试

  • 使用VI.Spyon的VUE内部测试,默认情况下,Pinia嘲笑所有方法。您可以在测试中直接执行此操作:

    const userStore = useUserStore();

    期望(usersstore.resetstatus).tohavebeencalledtimes(1);

我为所有BebeforeMount做到这一点,并在商店打电话给HTTP调用

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一起使用。
我所有组件的钩子都是异步和回归承诺。

I use vitest, pinia and testing-library (based on vue-test-utils).
IMHO, there is no need :

  • to mock onMounted : you're goal is not to check that onMounted is called => this is Vue internal test

  • to use vi.spyOn, by default, pinia mocks all methods. You could directly do this in your test :

    const usersStore = useUsersStore();

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

I do this for all onBeforeMount, onMounted that calls http calls from the store

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

And in the test

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

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

I think this should work with vue-test-utils in the same way.
All my component's hooks are async and return promise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文