Основные концепции unit-тестирования компонентов

Unit-тестирование компонентов Vue фокусируется на проверке их поведения изолированно, без зависимости от внешних сервисов или глобального состояния приложения. Библиотека Vue Test Utils (VTU) предоставляет инструменты для рендеринга компонентов, взаимодействия с ними и проверки их состояния.


Монтаж компонентов

Mount и shallowMount — два ключевых метода для рендеринга компонентов:

  • mount создаёт полный экземпляр компонента, включая все дочерние компоненты. Используется, когда необходимо проверить полное поведение или взаимодействие с вложенными компонентами.

  • shallowMount рендерит компонент, но заменяет дочерние компоненты на заглушки. Подходит для изолированного тестирования логики компонента без влияния вложенных компонентов.

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

const wrapper = mount(MyComponent);
const shallowWrapper = shallowMount(MyComponent);

Ключевой момент: выбор между mount и shallowMount зависит от цели теста. Для unit-тестов чаще используется shallowMount.


Поиск элементов внутри компонента

VTU предоставляет несколько методов для поиска элементов:

  • wrapper.find(selector) — возвращает первый найденный элемент, соответствующий селектору.
  • wrapper.findAll(selector) — возвращает массив всех совпадений.
  • wrapper.get(selector) — аналог find, но выбрасывает ошибку, если элемент не найден, что удобно для строгих проверок.

Селекторы могут быть CSS-классами, тегами, атрибутами и даже именами компонентов:

const button = wrapper.find('button');
const inputs = wrapper.findAll('input');
const custom = wrapper.findComponent({ name: 'ChildComponent' });

Взаимодействие с компонентом

Симуляция событий и изменение состояния компонента — центральная часть unit-тестирования:

  • trigger(event) — инициирует событие на элементе.
  • setValue(value) — устанавливает значение для input-элементов.
  • setProps(props) — обновляет свойства компонента.
  • setData(data) — обновляет локальное состояние компонента.

Пример:

const input = wrapper.find('input');
await input.setValue('новое значение');
await wrapper.find('form').trigger('submit');

await wrapper.setProps({ isVisible: true });
await wrapper.setData({ count: 5 });

Важно: большинство операций изменяет реактивное состояние асинхронно, поэтому await используется для ожидания обновления DOM.


Проверка состояния и DOM

VTU позволяет проверять как реактивное состояние компонента, так и видимый DOM:

  • wrapper.text() — возвращает текстовое содержимое компонента.
  • wrapper.html() — возвращает HTML.
  • wrapper.classes() — массив классов корневого элемента.
  • wrapper.attributes() — объект атрибутов элемента.
  • wrapper.vm — доступ к экземпляру Vue и его реактивным данным, методам и вычисляемым свойствам.

Пример проверки:

expect(wrapper.text()).toContain('Привет');
expect(wrapper.classes()).toContain('active');
expect(wrapper.vm.count).toBe(5);

Работа с событиями и эмиттерами

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

  • wrapper.emitted() — возвращает объект с событиями, которые были вызваны.
  • wrapper.emitted('eventName') — массив вызовов конкретного события.
  • wrapper.emitted('eventName')[0] — аргументы первого вызова события.

Пример:

await wrapper.find('button').trigger('click');
expect(wrapper.emitted()).toHaveProperty('submit');
expect(wrapper.emitted('submit')[0]).toEqual([{ success: true }]);

Ключевой момент: unit-тест должен проверять, что компонент правильно эмиттит события при изменении состояния или взаимодействии с пользователем.


Мока и заглушки

Для изоляции тестов часто используют моки:

  • Заглушки компонентов (stubs): заменяют дочерние компоненты простыми версиями, чтобы избежать тестирования их логики.

    const wrapper = shallowMount(ParentComponent, {
      stubs: ['ChildComponent']
    });
  • Моки методов (methods): позволяют заменить методы компонента на функции-заглушки.

    const wrapper = mount(MyComponent, {
      methods: {
        fetchData: jest.fn()
      }
    });
  • Моки плагинов и глобальных свойств (global): можно подменить Vue Router, Vuex или любые глобальные свойства.

    const wrapper = mount(MyComponent, {
      global: {
        mocks: {
          $t: (msg) => msg
        }
      }
    });

Асинхронные компоненты и обновление DOM

Vue обновляет DOM асинхронно. Для корректного тестирования асинхронных операций необходимо использовать await nextTick() или асинхронные ожидания:

import { nextTick } from 'vue';

await wrapper.setData({ count: 1 });
await nextTick();
expect(wrapper.text()).toContain('1');

Использование await обеспечивает, что все реактивные вычисления завершены до проверки состояния компонента.


Стратегии организации unit-тестов

  • Модульные тесты компонентов: отдельный файл для каждого компонента. Тестируются props, методы, события и рендеринг.
  • Группировка по функциям: блоки describe объединяют тесты по аспектам поведения: рендеринг, методы, события.
  • Явные проверки: использовать get и строгие ожидания, чтобы тест падал при любых несоответствиях.
  • Минимизация внешних зависимостей: все внешние сервисы должны быть замоканы.
describe('MyComponent', () => {
  it('рендерит текст', () => {
    const wrapper = shallowMount(MyComponent);
    expect(wrapper.text()).toContain('Привет');
  });

  it('вызывает событие submit', async () => {
    const wrapper = shallowMount(MyComponent);
    await wrapper.find('button').trigger('click');
    expect(wrapper.emitted('submit')).toHaveLength(1);
  });
});

Интеграция с Jest

Vue Test Utils тесно интегрируется с Jest. Используются стандартные matchers:

  • toBe / toEqual — проверка значения.
  • toContain — проверка текста или массива.
  • toHaveLength — проверка количества элементов.
  • toHaveProperty — проверка наличия события или свойства.

Совмещение VTU и Jest обеспечивает надёжные unit-тесты компонентов с полной поддержкой реактивности, событий и асинхронных изменений DOM.


Эта основа позволяет строить детальные и изолированные unit-тесты для Vue-компонентов, охватывая их рендеринг, реактивное состояние, методы, события и взаимодействие с дочерними компонентами.