Функция Vue Pinia не определена в onMounted при запуске модульного теста
У меня есть компонент и магазин Pinia, который содержит состояние и некоторые действия. Код отлично работает в браузере и в тестах E2E (кипарис), но не работает в модульных тестах. Я использую vue-testing-utils и vitest.
Функцию хранилища можно назвать отличной из модульного теста при нажатии кнопки, но если функция находится в смонтированном или основном скрипте, она не проходит тест.
источник/компоненты/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>
источник/магазины/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'
},
},
})
источник /компоненты/ тесты / 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 разные ошибки. Первый — это консольный журнал, когда функция пытается запуститься, а второй — это то, что возвращает 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>
Я знаю, что этот пример немного базовый и на самом деле не служит цели, но мне интересно, как я могу иметь функции хранения внутри
2 ответа
Я использую vitest, pinia и библиотеку тестирования (на основе vue-test-utils). ИМХО, нет необходимости:
издеваться над onMounted: ваша цель не проверять, что onMounted вызывается => это внутренний тест Vue
Чтобы использовать vi.spyOn, по умолчанию Pinia имитирует все методы. Вы можете сделать это непосредственно в своем тесте:
constusersStore = useUsersStore();
ожидать(usersStore.resetStatus).toHaveBeenCalledTimes(1);
Я делаю это для всех onBeforeMount и onMounted, которые вызывают 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 таким же образом. Все перехватчики моего компонента являются асинхронными и возвращают обещание.
Возможно, это может быть полезно для вас:
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)
})
})