В тестировании компонентов Vue с помощью Vue Test Utils критически важно корректно работать с асинхронными операциями, поскольку современные приложения часто взаимодействуют с API, используют таймеры или промисы. Ошибки, возникающие в этих операциях, могут приводить к непредсказуемым результатам тестов, поэтому необходимо научиться их правильно перехватывать и проверять.
Компоненты Vue часто содержат методы, возвращающие промисы:
export default {
methods: {
async fetchData() {
const response = await fetch('/api/data');
if (!response.ok) throw new Error('Ошибка при получении данных');
return await response.json();
}
}
}
При тестировании таких методов нужно учитывать, что ошибка
внутри промиса не выбрасывается немедленно, а лишь отклоняет
промис. Попытка вызвать метод без использования await или
.catch приведет к неперехваченной ошибке и падению
теста.
async/await в тестахДля перехвата ошибок асинхронных методов применяется
try/catch внутри асинхронной функции теста:
import { mount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
test('метод fetchData выбрасывает ошибку при неправильном ответе', async () => {
const wrapper = mount(MyComponent);
global.fetch = jest.fn(() =>
Promise.resolve({ ok: false })
);
try {
await wrapper.vm.fetchData();
} catch (e) {
expect(e).toBeInstanceOf(Error);
expect(e.message).toBe('Ошибка при получении данных');
}
});
Ключевые моменты:
await.try/catch..rejectsJest предоставляет удобный способ тестирования промисов с ожидаемым
отклонением через .rejects:
test('fetchData возвращает ошибку', async () => {
const wrapper = mount(MyComponent);
global.fetch = jest.fn(() =>
Promise.resolve({ ok: false })
);
await expect(wrapper.vm.fetchData()).rejects.toThrow('Ошибка при получении данных');
});
Преимущества подхода .rejects:
try/catch.Vue обновляет DOM асинхронно. После вызова асинхронного метода компонента необходимо дождаться следующего «типа цикла реактивности», чтобы проверить состояние компонента:
import { nextTick } from 'vue';
test('ошибка отображается в компоненте', async () => {
const wrapper = mount(MyComponent);
wrapper.vm.fetchData = jest.fn(() => Promise.reject(new Error('Ошибка')));
await wrapper.vm.fetchData().catch(() => {});
await nextTick();
expect(wrapper.text()).toContain('Произошла ошибка');
});
Особенности:
nextTick используется для ожидания обновления DOM после
изменения состояния.catch необходим, чтобы тест не падал из-за
необработанного промиса.wrapper.text() или
другие селекторы.Для контроля ошибок рекомендуется мокировать внешние зависимости, например, API-запросы:
jest.mock('@/api', () => ({
getData: jest.fn()
}));
import { getData } from '@/api';
import MyComponent from '@/components/MyComponent.vue';
import { mount } from '@vue/test-utils';
test('fetchData правильно обрабатывает ошибку API', async () => {
getData.mockRejectedValue(new Error('Ошибка API'));
const wrapper = mount(MyComponent);
await wrapper.vm.fetchData().catch(() => {});
await nextTick();
expect(wrapper.text()).toContain('Произошла ошибка');
});
Преимущества моков:
Если метод использует setTimeout или другие таймеры,
нужно использовать Jest Fake Timers:
jest.useFakeTimers();
test('ошибка через таймер', async () => {
const wrapper = mount(MyComponent);
wrapper.vm.delayedError = () =>
new Promise((_, reject) => setTimeout(() => reject(new Error('Ошибка таймера')), 1000));
const promise = wrapper.vm.delayedError();
jest.advanceTimersByTime(1000);
await expect(promise).rejects.toThrow('Ошибка таймера');
});
Особенности:
jest.useFakeTimers() позволяет контролировать
таймеры.jest.advanceTimersByTime ускоряет время ожидания.await или
.then/.catch..rejects делает код
лаконичным.nextTick.Эта практика позволяет создавать надежные тесты компонентов Vue, корректно реагирующих на ошибки в асинхронных операциях, и обеспечивает стабильность тестового покрытия в сложных сценариях с API и таймерами.