Мокирование зависимостей

Мокирование зависимостей — ключевой инструмент при тестировании приложений на SvelteKit. Оно позволяет изолировать компоненты, контролировать поведение внешних модулей и предотвращать вызовы реальных API во время тестов. В SvelteKit мокирование применяется как на уровне сервисов, так и на уровне компонентов.

Подходы к мокированию

1. Использование Jest для моков модулей

Jest предоставляет встроенные возможности для мокирования ES-модулей и CommonJS. В SvelteKit это особенно полезно для мокирования утилит, fetch-запросов и внешних библиотек.

// __mocks__/api.js
export const fetchData = jest.fn(() => Promise.resolve({ data: 'mocked data' }));

// test.spec.js
import { fetchData } from '../api.js';
import Component from '../Component.svelte';
import { render } from '@testing-library/svelte';

jest.mock('../api.js');

test('отображает данные из мока', async () => {
  fetchData.mockResolvedValue({ data: 'тестовые данные' });
  const { findByText } = render(Component);
  expect(await findByText('тестовые данные')).toBeInTheDocument();
});

Ключевые моменты:

  • jest.mock() позволяет автоматически заменить модуль на его мок-версию.
  • mockResolvedValue и mockRejectedValue помогают управлять возвращаемыми промисами.
  • Моки можно комбинировать с beforeEach для сброса состояния перед каждым тестом.

2. Мокирование HTTP-запросов

SvelteKit активно использует функцию fetch для взаимодействия с внешними API. В тестах fetch можно заменить на мок, чтобы контролировать ответы и задержки.

import Component from '../Component.svelte';
import { render } from '@testing-library/svelte';

beforeEach(() => {
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ items: ['item1', 'item2'] })
    })
  );
});

test('рендерит элементы из fetch', async () => {
  const { findByText } = render(Component);
  expect(await findByText('item1')).toBeInTheDocument();
  expect(await findByText('item2')).toBeInTheDocument();
});

Особенности:

  • Мок fetch заменяет глобальный объект, что позволяет полностью контролировать API-ответы.
  • Можно имитировать ошибки сети с помощью Promise.reject().

3. Мокирование зависимостей через DI (Dependency Injection)

SvelteKit поддерживает паттерны инъекции зависимостей через контекст компонента (setContext, getContext) и сервисные модули. Это облегчает замену сервисов на мок-версии.

// service.js
export function getUser() {
  return fetch('/api/user').then(res => res.json());
}

// Component.svelte
<script>
  import { getContext } from 'svelte';
  const service = getContext('userService');
  let user;
  service.getUser().then(data => user = data);
</script>

// test.spec.js
import { render } from '@testing-library/svelte';
import Component from '../Component.svelte';

const mockService = {
  getUser: jest.fn(() => Promise.resolve({ name: 'Тестовый пользователь' }))
};

test('использует мок сервис', async () => {
  const { findByText } = render(Component, {
    context: new Map([['userService', mockService]])
  });
  expect(await findByText('Тестовый пользователь')).toBeInTheDocument();
});

Преимущества DI-моков:

  • Полная изоляция компонентов от реальных сервисов.
  • Простое управление состоянием моков.
  • Легкость интеграции с Jest или Vitest.

4. Мокирование Store

SvelteKit активно использует reactive store (writable, readable) для управления состоянием. В тестах можно создавать мок-версии store с предопределёнными значениями.

// store.js
import { writable } from 'svelte/store';
export const userStore = writable(null);

// Component.svelte
<script>
  import { userStore } from './store.js';
  let user;
  $: $userStore, user = $userStore;
</script>

// test.spec.js
import { render } from '@testing-library/svelte';
import Component from '../Component.svelte';
import { userStore } from '../store.js';

test('отображает мок store', async () => {
  userStore.set({ name: 'Мок пользователь' });
  const { findByText } = render(Component);
  expect(await findByText('Мок пользователь')).toBeInTheDocument();
});

Преимущества мокирования store:

  • Контроль реактивного состояния без необходимости инициализировать полный компонент.
  • Позволяет тестировать реакции на изменение состояния в изоляции.

5. Использование сторонних библиотек для мокирования

  • MSW (Mock Service Worker) позволяет создавать моки для HTTP-запросов на уровне сети, что имитирует реальные API без изменения кода.
  • Sinon.js полезен для шпионов, stub’ов и моков функций, особенно при сложных асинхронных сценариях.
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import Component from '../Component.svelte';
import { render } from '@testing-library/svelte';

const server = setupServer(
  rest.get('/api/items', (req, res, ctx) => res(ctx.json(['item1', 'item2'])))
);

beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());

test('рендерит данные MSW', async () => {
  const { findByText } = render(Component);
  expect(await findByText('item1')).toBeInTheDocument();
});

Особенности MSW:

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

Рекомендации по организации моков

  1. Разделение моков по папкам__mocks__ для модулей, отдельные файлы для store и сервисов.
  2. Централизованное управление моками — облегчает их переиспользование между тестами.
  3. Сброс состояния моков между тестами — предотвращает нежелательное накопление данных.
  4. Комбинирование подходов — мокирование fetch + store + DI позволяет полностью изолировать компонент.

Мокирование зависимостей в SvelteKit — это не просто техника, а фундаментальная практика, позволяющая создавать надёжные, предсказуемые и быстрые тесты без зависимости от внешних сервисов.