Тестирование Composition API

Composition API во Vue.js предоставляет мощные возможности для организации логики компонентов с помощью функций setup(), реактивных переменных и эффектов. Тестирование таких компонентов требует понимания, как эти реактивные сущности взаимодействуют с рендерингом и поведением приложения.

Реактивные переменные и их тестирование

В Composition API состояние компонента обычно создаётся через ref() или reactive(). Для тестирования важно проверять не только начальные значения, но и их реакцию на изменения.

import { ref } from 'vue';

export function useCounter() {
  const count = ref(0);
  const increment = () => count.value++;
  return { count, increment };
}

Тестирование такой функции может выполняться без рендера компонента, используя прямое обращение к возвращаемым значениям:

import { useCounter } from './useCounter';

test('increment увеличивает count', () => {
  const { count, increment } = useCounter();
  expect(count.value).toBe(0);
  increment();
  expect(count.value).toBe(1);
});

Ключевой момент — ref хранит значение в поле .value, и именно его нужно проверять в тестах.

Тестирование компонентов с setup()

Компоненты, использующие setup(), можно тестировать через рендеринг с помощью библиотек вроде Vue Test Utils. Основная задача — проверять, как состояние влияет на DOM и взаимодействие пользователя.

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

test('отображение и увеличение счётчика', async () => {
  const wrapper = mount(CounterComponent);
  expect(wrapper.text()).toContain('0');
  await wrapper.find('button').trigger('click');
  expect(wrapper.text()).toContain('1');
});

Изоляция логики с composables

Composition API поощряет создание reusable функций (composables). Для таких функций тестирование проще, так как они не зависят от DOM:

export function useToggle(initial = false) {
  const state = ref(initial);
  const toggle = () => state.value = !state.value;
  return { state, toggle };
}

Тестирование:

import { useToggle } from './useToggle';

test('toggle меняет состояние', () => {
  const { state, toggle } = useToggle();
  expect(state.value).toBe(false);
  toggle();
  expect(state.value).toBe(true);
});

Тестирование асинхронной логики

Асинхронные операции в setup() могут выполняться через watchEffect, onMounted или обычные функции с async/await. Для тестирования таких сценариев важно использовать асинхронные тесты.

import { ref, onMounted } from 'vue';

export function useFetchData(fetcher) {
  const data = ref(null);
  const error = ref(null);

  onMounted(async () => {
    try {
      data.value = await fetcher();
    } catch (e) {
      error.value = e;
    }
  });

  return { data, error };
}

Тестирование:

import { useFetchData } from './useFetchData';
import { nextTick } from 'vue';

test('fetchData получает данные', async () => {
  const fetcher = jest.fn().mockResolvedValue('result');
  const { data } = useFetchData(fetcher);
  await nextTick();
  expect(data.value).toBe('result');
});

Использование nextTick гарантирует, что реактивные обновления завершились перед проверкой значений.

Mocking зависимостей

Компоненты с Composition API часто используют внешние сервисы или composables. Для тестирования необходимо изолировать такие зависимости через мок-функции:

jest.mock('./api', () => ({
  fetchUser: jest.fn().mockResolvedValue({ name: 'Alice' })
}));

Это позволяет тестировать логику компонента без реальных сетевых запросов и контролировать возвращаемые данные.

Совмещение реактивного состояния и DOM

Для сложных компонентов важно проверять взаимодействие реактивного состояния с шаблоном. Vue Test Utils предоставляет методы find, text, trigger, а также доступ к vm для проверки внутренних данных:

const wrapper = mount(Component);
wrapper.vm.someRef.value = 42;
await nextTick();
expect(wrapper.text()).toContain('42');

Практика unit-тестирования vs e2e

  • Unit-тесты: проверяют composables, методы и реактивные переменные без рендера компонента. Быстрые, изолированные, покрывают бизнес-логику.
  • Компонентные тесты: проверяют setup() с рендером. Подтверждают корректность DOM и взаимодействий.
  • E2E: проверяют полный поток приложения, включая интеграцию с backend и маршрутизацию. Для Composition API такие тесты актуальны для комплексных компонентов.

Советы по стабильному тестированию

  1. Избегать прямого доступа к DOM в unit-тестах: использовать только реактивные значения.
  2. Разделять composables и компоненты: это упрощает тестирование.
  3. Использовать nextTick для реактивных обновлений: гарантирует корректность проверок после изменений состояния.
  4. Мокать внешние зависимости: API, хранилища, сторонние библиотеки.

Тестирование Composition API требует внимания к реактивности и асинхронности. Изоляция логики через composables делает код более предсказуемым и легко проверяемым, что позволяет создавать надёжные и масштабируемые приложения на Vue.js.