Vue test utils - тестирование async функции и загрузки данных

vue test utils - Тестирование async функции и загрузки данных

Компонент:

<template>
  <div>
    <p v-if="loading">Загрузка...</p>
    <p v-else>{{ data }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const data = ref('');
const loading = ref(true);

const fetchData = async () => {
  // Имитация загрузки
  await new Promise(resolve => setTimeout(resolve, 100));
  data.value = 'Данные загружены';
  loading.value = false;
};

onMounted(() => {
  fetchData();
});
</script>

Задание:

Проверьте, что сначала отображается "Загрузка...", а после завершения fetchData — "Данные загружены".

Для того чтобы управлять таймерами в vitest есть:

vi.runAllTimers() - Этот метод выполняет все запланированные макросы (макротаски), которые включают:

Рекурсивные/вложенные таймеры

setTimeout()

setInterval()

setImmediate() (в Node.js)

Использование:

import { vi } from 'vitest'
// Включаем моки таймеров
vi.useFakeTimers()
// Запускаем все таймеры
vi.runAllTimers()

Так же, при тестировании компонентов с промисами, необходимо дождаться их выполнения:

await Promise.resolve();
await wrapper.vm.$nextTick();

Решение:

import { mount } from '@vue/test-utils';
import { vi, it, describe } from 'vitest';
import Async from '@/components/Async.vue';

describe('Async.vue', () => {
    it('отображает загрузку, затем данные', async () => {
        vi.useFakeTimers();
        const wrapper = mount(Async);

        expect(wrapper.find('p').text()).toBe('Загрузка...');

        vi.runAllTimers(); // выполняем все таймеры

        await Promise.resolve(); // выполняем промис, без этого сам таймер выполнится а вот промис нет
        await wrapper.vm.$nextTick(); // обновление DOM

        expect(wrapper.find('p').text()).toBe('Данные загружены');
        vi.useRealTimers();
    })
})