Моки и стабы

Slim.js предоставляет мощный инструмент для создания легковесных веб-компонентов с декларативным синтаксисом и реактивным состоянием. Одной из ключевых задач при разработке приложений на Slim.js является тестирование компонентов. Для этого широко применяются моки и стабы, позволяющие изолировать части приложения и имитировать поведение зависимостей.


Понятие моков и стабов

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

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

Отличия:

Параметр Мок Стаб
Основная цель Проверка взаимодействий Замена зависимости для теста
Возвращаемые данные Можно настраивать и проверять Обычно фиксированные данные
Слежение вызовов Да Нет (в базовом варианте)

Использование моков в Slim.js

Slim.js ориентирован на компоненты с реактивным состоянием. Для тестирования таких компонентов моки позволяют:

  1. Подменить сервисы или API.
  2. Отслеживать вызовы методов.
  3. Проверять корректность передачи данных между компонентами.

Пример использования мок-сервиса для Slim-компонента:

import { Slim } from 'slim-js';

class UserServiceMock {
  constructor() {
    this.getUserCalls = 0;
  }
  
  getUser(id) {
    this.getUserCalls++;
    return Promise.resolve({ id, name: 'Mock User' });
  }
}

class UserComponent extends Slim {
  static get observedAttributes() { return ['user-id']; }

  constructor() {
    super();
    this.user = null;
    this.userService = new UserServiceMock();
  }

  connectedCallback() {
    super.connectedCallback();
    this.fetchUser();
  }

  fetchUser() {
    this.userService.getUser(this.userId).then(user => {
      this.user = user;
      this.update();
    });
  }

  get userId() {
    return this.getAttribute('user-id');
  }

  render() {
    return `<div>${this.user ? this.user.name : 'Loading...'}</div>`;
  }
}

customElements.define('user-component', UserComponent);

В данном примере UserServiceMock отслеживает количество вызовов метода getUser, что позволяет проверять, как компонент взаимодействует с сервисом без обращения к реальному API.


Стабы для Slim-компонентов

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

Пример стаба:

class UserServiceStub {
  getUser() {
    return Promise.resolve({ id: 1, name: 'Stub User' });
  }
}

class UserComponentWithStub extends UserComponent {
  constructor() {
    super();
    this.userService = new UserServiceStub();
  }
}

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


Интеграция с тестовыми фреймворками

Slim.js хорошо сочетается с Jest, Mocha и другими инструментами для юнит-тестирования. Для моков и стабов в тестах применяются:

  • jest.fn() — создание мок-функций с возможностью отслеживания вызовов.
  • sinon.stub() — создание стабов с фиксированными результатами.

Пример интеграции с Jest:

import { UserComponent } from './UserComponent.js';

test('UserComponent вызывает getUser', async () => {
  const mockService = { getUser: jest.fn().mockResolvedValue({ id: 1, name: 'Test User' }) };
  const component = new UserComponent();
  component.userService = mockService;
  component.setAttribute('user-id', '1');

  component.connectedCallback();
  await new Promise(r => setTimeout(r, 0));

  expect(mockService.getUser).toHaveBeenCalledWith('1');
  expect(component.user.name).toBe('Test User');
});

Здесь мок позволяет проверить, что метод getUser был вызван с правильным аргументом, а компонент корректно обновил состояние.


Динамические моки и стабы

Slim.js позволяет динамически подменять зависимости даже после создания компонента. Это полезно для сценариев, когда требуется эмулировать разные состояния сервиса:

component.userService = new class {
  getUser() { return Promise.resolve({ id: 2, name: 'Dynamic Mock' }); }
}();

component.fetchUser();

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


Рекомендации по использованию

  • Для проверки вызовов методов и взаимодействия компонентов применяются моки.
  • Для упрощения данных и предсказуемого поведения лучше использовать стабы.
  • Для сложных компонентов Slim.js комбинирование моков и стабов позволяет тестировать логику, не трогая реальный DOM и внешние зависимости.
  • Всегда использовать асинхронные методы с await или Promise, чтобы правильно тестировать реактивное состояние компонента.

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