Тестирование компонентов с ошибками

Тестирование компонентов Vue часто требует проверки поведения при возникновении ошибок. В реальных приложениях ошибки могут появляться в различных слоях: при рендеринге шаблона, вызове методов, работе с асинхронными функциями или сторонними API. Vue Test Utils предоставляет набор инструментов для точного контроля и проверки таких ситуаций.

Обработка ошибок рендеринга

Ошибки во время рендеринга компонента могут быть вызваны отсутствием пропсов, неправильным синтаксисом шаблона или некорректными вычисляемыми свойствами. Для проверки таких ошибок используется метод mount и глобальный хук errorHandler:

import { mount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';

let capturedError = null;

const wrapper = mount(MyComponent, {
  global: {
    config: {
      errorHandler(err) {
        capturedError = err;
      }
    }
  }
});

expect(capturedError).toBeInstanceOf(Error);
expect(capturedError.message).toContain('prop "name" is required');

Ключевой момент: Vue позволяет перехватывать ошибки через config.errorHandler, что дает возможность тестировать некорректные состояния компонентов без падения всего теста.

Проверка ошибок методов и событий

Методы компонента могут выбрасывать исключения при неверных данных или сбоях во внешних сервисах. Для их тестирования часто используют jest.fn() или vi.fn() (Vite):

import { mount } from '@vue/test-utils';
import UserForm from '@/components/UserForm.vue';

const wrapper = mount(UserForm);

wrapper.vm.submitForm = jest.fn(() => {
  throw new Error('Invalid input');
});

expect(() => wrapper.vm.submitForm()).toThrow('Invalid input');

Ключевой момент: Вызов метода напрямую через wrapper.vm позволяет контролировать и проверять поведение при ошибках без необходимости эмулировать весь пользовательский ввод.

Тестирование асинхронных ошибок

Асинхронные функции часто используются для запросов к API. Ошибки в них нужно обрабатывать через async/await и try/catch в тестах:

import { mount } from '@vue/test-utils';
import DataLoader from '@/components/DataLoader.vue';

const fetchData = jest.fn().mockRejectedValue(new Error('Network error'));

const wrapper = mount(DataLoader, {
  props: { fetchData }
});

await expect(wrapper.vm.loadData()).rejects.toThrow('Network error');

Ключевой момент: Использование mockRejectedValue позволяет симулировать сбои в асинхронных операциях и проверять, корректно ли компонент реагирует на них.

Ошибки дочерних компонентов

В сложных приложениях ошибки могут возникать в дочерних компонентах. Vue Test Utils позволяет подменять дочерние компоненты заглушками и отслеживать их ошибки:

import { mount } from '@vue/test-utils';
import ParentComponent from '@/components/ParentComponent.vue';

const ErrorChild = {
  template: '<div></div>',
  mounted() {
    throw new Error('Child error');
  }
};

let capturedError = null;

const wrapper = mount(ParentComponent, {
  global: {
    components: { ChildComponent: ErrorChild },
    config: {
      errorHandler(err) {
        capturedError = err;
      }
    }
  }
});

expect(capturedError).toBeInstanceOf(Error);
expect(capturedError.message).toBe('Child error');

Ключевой момент: Подмена компонентов упрощает контроль ошибок, возникающих в глубине иерархии компонентов, без необходимости тестировать их реализацию напрямую.

Проверка пользовательского отображения ошибок

Компоненты часто должны отображать сообщение об ошибке пользователю. Для проверки визуальной части используют методы find и text:

import { mount } from '@vue/test-utils';
import Notification from '@/components/Notification.vue';

const wrapper = mount(Notification, {
  props: { message: 'Something went wrong', type: 'error' }
});

const alert = wrapper.find('.alert-error');
expect(alert.exists()).toBe(true);
expect(alert.text()).toBe('Something went wrong');

Ключевой момент: Тестирование не ограничивается только перехватом исключений. Важно проверять, что интерфейс адекватно информирует пользователя о возникшей ошибке.

Логирование и подавление ошибок в тестах

Иногда ошибки нужно подавлять или проверять их логирование, чтобы они не прерывали выполнение тестов:

const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

const wrapper = mount(MyComponent);
// компонент вызывает console.error при ошибке
expect(consoleErrorSpy).toHaveBeenCalled();

consoleErrorSpy.mockRestore();

Ключевой момент: Подмена функций console.error или console.warn позволяет тестам оставаться чистыми, при этом фиксируя факт возникновения ошибок.

Рекомендации по структуре тестов с ошибками

  1. Изолировать источник ошибки: тестировать конкретный метод, пропс или дочерний компонент.
  2. Использовать заглушки и моки: это снижает зависимость от сторонних сервисов.
  3. Проверять пользовательский интерфейс: убедиться, что ошибки корректно отображаются.
  4. Отслеживать глобальные обработчики ошибок: config.errorHandler полезен для рендеринга и ошибок дочерних компонентов.
  5. Асинхронные проверки: всегда использовать await и .rejects.toThrow для промисов.

Тестирование компонентов с ошибками обеспечивает стабильность приложения и предотвращает неожиданные сбои в продакшене. Применение Vue Test Utils позволяет создавать детальные, контролируемые сценарии, проверяя как внутренние ошибки компонентов, так и их отображение пользователю.