Моки и стабы в тестах

Моки и стабы — это два типа объектов, используемых в юнит-тестах для имитации работы зависимостей, с которыми взаимодействуют тестируемые компоненты. Они позволяют контролировать поведение зависимостей, изолировать тестируемую часть кода и эффективно проверять логику без необходимости интегрировать все реальные сервисы или внешние библиотеки. В контексте фреймворка Svelte они играют важную роль в тестировании компонентов и приложений, обеспечивая легкость в проведении тестов с моками и стабами.

Что такое мок?

Мок (от англ. mock) — это объект, который имитирует поведение реальной зависимости, но имеет заранее заданную логику или фиксированные данные. Моки используются для проверки взаимодействия компонента с другими частями системы, например, с API или сервисами.

В отличие от стабов, моки не просто заменяют часть функционала, но и позволяют проверять, как конкретный код взаимодействует с этим функционалом. Это означает, что моки могут отслеживать, были ли вызваны определённые методы, сколько раз, с какими параметрами и так далее.

Пример использования моков

Допустим, есть компонент, который зависит от сервиса для получения данных. Тестирование такого компонента без мока потребовало бы реального вызова API, что может быть не только медленно, но и нецелесообразно.

// Компонент, который зависит от API-сервиса
<script>
  import { onMount } from 'svelte';
  let data = [];

  async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    data = await response.json();
  }

  onMount(fetchData);
</script>

Для того чтобы протестировать этот компонент, можно создать мок для функции fetch.

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

test('компонент получает данные через API', async () => {
  // Мокаем функцию fetch
  global.fetch = jest.fn().mockResolvedValue({
    json: jest.fn().mockResolvedValue([{ id: 1, name: 'Test' }])
  });

  const { getByText } = render(MyComponent);
  
  // Проверяем, что данные отобразились
  await waitFor(() => getByText('Test'));
  expect(fetch).toHaveBeenCalledWith('https://api.example.com/data');
});

В этом примере мы замещаем реальный вызов fetch на мок, который сразу возвращает фиксированные данные. Это позволяет проверить логику работы компонента без необходимости обращения к реальному API.

Что такое стаб?

Стаб (от англ. stub) — это объект, который имитирует поведение зависимой компоненты или функции, но в отличие от мока, стаб не отслеживает взаимодействие с ним. Его задача — вернуть заранее заданные данные или результат выполнения. В отличие от мока, стаб не предоставляет функционала для проверки вызовов методов, а только заменяет реальную зависимость.

Пример использования стабов

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

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

test('компонент взаимодействует с базой данных', async () => {
  // Стабируем работу с базой данных
  const db = {
    getData: jest.fn().mockReturnValue([{ id: 1, name: 'Test' }])
  };

  const { getByText } = render(MyComponent, { props: { db } });

  // Проверяем, что данные отобразились
  await waitFor(() => getByText('Test'));
});

В данном случае стаб db.getData всегда возвращает фиксированный результат, что позволяет протестировать логику компонента, не взаимодействуя с реальной базой данных.

Основные отличия между моками и стабами

Хотя моки и стабы имеют сходную цель — заменить реальные зависимости в тестах, они обладают различиями, которые могут определять выбор в зависимости от ситуации:

  1. Поведение:

    • Мок используется для проверки взаимодействия с зависимостью. Он отслеживает вызовы методов, проверяет, сколько раз они были вызваны и с какими параметрами.
    • Стаб заменяет функциональность зависимости и не отслеживает количество или параметры вызовов.
  2. Тестируемые аспекты:

    • Мок помогает проверять, как компонент взаимодействует с зависимыми объектами, и может проверять детали, такие как параметры вызова и количество вызовов.
    • Стаб используется для имитации возвращаемых данных, не проверяя поведение компонентов.
  3. Использование:

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

Моки и стабы в Svelte

В Svelte тестах, как и в других приложениях на JavaScript, моки и стабы можно использовать для замены внешних зависимостей, таких как API-запросы, работа с базами данных или любые другие сервисы, которые сложно тестировать в условиях реального выполнения. С помощью инструментов, таких как jest или sinon, можно удобно мокаить или стабировать функции.

Пример мокирования компонента с внешним сервисом:

import { render, fireEvent } from '@testing-library/svelte';
import MyComponent from './MyComponent.svelte';

// Мокаем функцию из внешнего сервиса
jest.mock('../services/api', () => ({
  fetchData: jest.fn().mockResolvedValue([{ id: 1, name: 'Test' }])
}));

test('компонент получает данные при клике', async () => {
  const { getByText } = render(MyComponent);

  // Симулируем клик, который должен инициировать запрос
  const button = getByText('Загрузить данные');
  await fireEvent.click(button);

  // Проверяем, что данные отобразились
  await waitFor(() => getByText('Test'));
});

Этот пример показывает, как мокаем внешний сервис с помощью jest.mock и проверяем поведение компонента при взаимодействии с ним.

Заключение

Моки и стабы играют важную роль в тестировании компонентов и приложений на Svelte, позволяя изолировать код и проверять его логику без необходимости взаимодействия с внешними сервисами. Они помогают сделать тесты более стабильными, быстрыми и предсказуемыми, что особенно важно при работе с большими и сложными приложениями.