Моки и стабы

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

Стаб (Stub)

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

Стаб может быть простым объектом с жестко зафиксированным результатом, например:

const databaseStub = {
  getUserById: (id) => ({ id, name: 'John Doe' }),
};

function getUserInfo(id, db) {
  const user = db.getUserById(id);
  return user.name;
}

console.log(getUserInfo(1, databaseStub)); // John Doe

В этом примере databaseStub — это стаб, который подставляет заранее заданные данные при вызове метода getUserById. Тестируемая функция не зависит от настоящей базы данных, а использует стаб в качестве замены.

Мок (Mock)

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

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

Пример использования мока с библиотекой, такой как jest:

const userService = {
  getUserById: jest.fn().mockReturnValue({ id: 1, name: 'John Doe' }),
};

function getUserInfo(id, service) {
  const user = service.getUserById(id);
  return user.name;
}

console.log(getUserInfo(1, userService)); // John Doe

// Проверка, что метод getUserById был вызван
expect(userService.getUserById).toHaveBeenCalledWith(1);

Здесь userService.getUserById является мок-методом. Помимо того, что он возвращает заранее заданные данные, мы также можем проверить, был ли вызван этот метод с нужными аргументами, сколько раз он был вызван и т.д.

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

  1. Цель:

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

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

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

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

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

Рассмотрим пример, где нужно протестировать компонент, который открывает модальное окно с использованием tingle.js. Вместо того чтобы взаимодействовать с реальным модальным окном в тестах, можно использовать стаб или мок.

Пример с моками для tingle.js:

import tingle from 'tingle.js';

const modalMock = jest.fn().mockImplementation(() => ({
  open: jest.fn(),
  close: jest.fn(),
}));

function openModal(modal) {
  modal.open();
}

const modal = modalMock();

openModal(modal);

expect(modal.open).toHaveBeenCalled();

В этом примере используется мок, чтобы отслеживать вызов метода open в объекте модального окна, создаваемом библиотекой tingle.js.

Когда использовать моки и стабы

  1. Когда использовать стаб:

    • Если зависимость вашего компонента не требует проверки поведения (например, простое возвращение данных).
    • Когда необходимо просто изолировать тестируемый компонент от реальных зависимостей.
  2. Когда использовать мок:

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

Сложности и подходы

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

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

Заключение

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