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

Middleware в Page.js представляет собой функции, которые выполняются до основного обработчика маршрута и позволяют обрабатывать запрос, изменять контекст или контролировать навигацию. Каждый middleware получает один параметр — объект ctx, содержащий информацию о текущем маршруте, и функцию next, вызывающую следующий middleware в цепочке.

page((ctx, next) => {
  console.log('Текущий путь:', ctx.path);
  next();
});
  • ctx.path — путь, по которому произошла навигация.
  • ctx.params — объект с динамическими параметрами маршрута.
  • ctx.querystring — строка запроса после ?.
  • ctx.state — объект для хранения состояния между middleware.

Регистрация middleware

Middleware можно добавлять глобально или для конкретных маршрутов.

Глобальный middleware:

page((ctx, next) => {
  ctx.state.startTime = Date.now();
  next();
});

Middleware для конкретного маршрута:

page('/profile/:id', authMiddleware, profileHandler);

function authMiddleware(ctx, next) {
  if (!ctx.state.user) {
    page.redirect('/login');
  } else {
    next();
  }
}

Важный момент: порядок регистрации имеет значение. Middleware выполняются строго в том порядке, в котором они объявлены.

Использование next() и контроль выполнения

Функция next() передаёт управление следующему middleware. Если она не вызвана, цепочка останавливается. Это используется для:

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

Пример асинхронного middleware:

page(async (ctx, next) => {
  ctx.state.data = await fetchData(ctx.params.id);
  next();
});

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

Тестирование middleware важно для подтверждения корректности обработки маршрутов, проверки логики авторизации, загрузки данных и управления состоянием.

Инструменты для тестирования

  • Jest или Mocha — фреймворки для модульного тестирования.
  • Sinon.js — для создания шпионов и заглушек.

Пример теста авторизации

import page from 'page';
import sinon from 'sinon';

describe('authMiddleware', () => {
  it('перенаправляет неавторизованного пользователя', () => {
    const ctx = { state: {}, path: '/profile', redirect: sinon.spy() };
    const next = sinon.spy();

    authMiddleware(ctx, next);

    sinon.assert.calledWith(ctx.redirect, '/login');
    sinon.assert.notCalled(next);
  });

  it('позволяет авторизованному пользователю продолжить', () => {
    const ctx = { state: { user: { name: 'Alice' } }, path: '/profile', redirect: sinon.spy() };
    const next = sinon.spy();

    authMiddleware(ctx, next);

    sinon.assert.calledOnce(next);
    sinon.assert.notCalled(ctx.redirect);
  });
});

Ключевые моменты тестирования:

  • Создание объекта ctx с необходимыми полями для теста.
  • Использование spy для отслеживания вызовов next() или redirect().
  • Проверка поведения middleware при разных состояниях (ctx.state.user есть/нет).

Тестирование асинхронных middleware

Асинхронные middleware требуют применения async/await и проверок после завершения промисов:

it('загружает данные перед продолжением', async () => {
  const ctx = { params: { id: 1 }, state: {} };
  const next = sinon.spy();

  await dataMiddleware(ctx, next);

  expect(ctx.state.data).toBeDefined();
  sinon.assert.calledOnce(next);
});

Составные цепочки middleware

Page.js поддерживает цепочки middleware, что позволяет строить многоуровневую обработку маршрутов:

page('/dashboard', authMiddleware, loadDashboardData, renderDashboard);

function loadDashboardData(ctx, next) {
  ctx.state.dashboard = fetchDashboard();
  next();
}

При тестировании таких цепочек важно:

  1. Проверять вызовы каждого middleware по отдельности.
  2. Контролировать порядок вызовов next().
  3. Эмулировать состояния ctx для разных сценариев (например, пользователь авторизован/не авторизован).

Mocking и изоляция

Для точного тестирования middleware часто используют mock-функции и объекты ctx, чтобы изолировать логику от внешних зависимостей, таких как серверные API или localStorage:

const mockCtx = {
  state: {},
  redirect: jest.fn(),
  params: {},
  path: '/test'
};
const next = jest.fn();

await someMiddleware(mockCtx, next);

expect(next).toHaveBeenCalled();
expect(mockCtx.state.modified).toBe(true);

Логирование и отладка

Для тестирования полезно интегрировать логирование вызовов middleware. Это помогает:

  • Проверить правильный порядок выполнения.
  • Отследить изменения в ctx.state.
  • Диагностировать проблемы при асинхронной загрузке данных.
page((ctx, next) => {
  console.log('Middleware 1:', ctx.path);
  next();
});

page((ctx, next) => {
  console.log('Middleware 2:', ctx.state);
  next();
});

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

Практические советы

  • Всегда проверять вызов next() в тестах, чтобы не допустить зависаний.
  • Использовать mock-объекты для контекста маршрута.
  • Разделять middleware на маленькие функции для легкого тестирования и переиспользования.
  • Асинхронные операции тестировать с async/await и моками API.

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