I have a component and a Pinia store that contains state and some operations. The code runs perfectly fine in browser and E2E (cypress) tests, but fails in unit tests. I'm using vue-testing-utils and vitest.
Calling a stored function from a unit test works fine when a button is clicked, but if the function is in an installed or main script, the test fails
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/Test/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() }) })
Unit test returns 2 different errors. The first is the 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 bit basic and doesn't really serve the purpose, but I'd like to know how to have a stored function in onMounted()
(or somewhere similar) without breaking all my unit tests .
P粉4516148342023-11-01 14:56:54
Maybe this will work for 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) }) })