HTTP middleware в Karma

Karma является инструментом для запуска тестов JavaScript в реальном браузере. Одной из ключевых возможностей Karma является использование HTTP middleware, которое позволяет перехватывать, модифицировать или обслуживать HTTP-запросы, поступающие от браузера, во время выполнения тестов. Это особенно важно при тестировании приложений с асинхронными запросами или при необходимости имитации серверной логики.

Установка Karma выполняется через npm:

npm install --save-dev karma karma-cli

Для работы с middleware необходимо подключить серверный компонент Karma — karma-server. Middleware в Karma регистрируется в конфигурационном файле karma.conf.js через опцию middleware и обрабатывается через массив функций, возвращающих обработчик (req, res, next).

module.exports = function(config) {
  config.set({
    frameworks: ['jasmine'],
    files: [
      'src/**/*.js',
      'test/**/*.spec.js'
    ],
    middleware: ['mockServer'],
    plugins: [
      'karma-jasmine',
      'karma-chrome-launcher',
      {
        'middleware:mockServer': ['factory', function() {
          return function(req, res, next) {
            if (req.url === '/api/data') {
              res.writeHead(200, { 'Content-Type': 'application/json' });
              res.end(JSON.stringify({ message: 'Hello from mock server' }));
            } else {
              next();
            }
          };
        }]
      }
    ]
  });
};

В этом примере создается псевдосервер, который перехватывает запросы к /api/data и возвращает статический JSON. Все остальные запросы передаются дальше по цепочке через next().


Структура middleware

Middleware в Karma представляют собой обычные функции Node.js, принимающие три аргумента:

  • req — объект запроса, содержащий информацию о URL, методе, заголовках и теле запроса.
  • res — объект ответа, через который можно отправить статус, заголовки и тело ответа.
  • next — функция для передачи запроса следующему middleware в цепочке.

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

function loggingMiddleware(req, res, next) {
  console.log(`[Karma Middleware] ${req.method} ${req.url}`);
  next();
}

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


Использование нескольких middleware

Karma позволяет подключать несколько middleware одновременно. Они выполняются в том порядке, в котором перечислены в конфигурации:

middleware: ['logger', 'mockApi', 'corsHandler'],
plugins: [
  {
    'middleware:logger': ['factory', () => (req, res, next) => {
      console.log(`${req.method} ${req.url}`);
      next();
    }],
  },
  {
    'middleware:mockApi': ['factory', () => (req, res, next) => {
      if (req.url.startsWith('/api')) {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ status: 'ok' }));
      } else next();
    }],
  },
  {
    'middleware:corsHandler': ['factory', () => (req, res, next) => {
      res.setHeader('Access-Control-Allow-Origin', '*');
      next();
    }],
  }
]

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


Асинхронные операции в middleware

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

const fs = require('fs').promises;

function fileMiddleware(req, res, next) {
  if (req.url === '/mock/file') {
    fs.readFile('./mock/data.json', 'utf-8')
      .then(data => {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(data);
      })
      .catch(err => {
        res.writeHead(500);
        res.end('Internal Server Error');
      });
  } else {
    next();
  }
}

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


Интеграция с тестами

Middleware напрямую влияет на тесты, поскольку браузер, запущенный через Karma, обращается к локальному серверу Karma для загрузки файлов и API-запросов. В Jasmine или Mocha тестах можно проверять поведение кода при разных ответах сервера:

describe('API Mock Test', () => {
  it('should return mock data', async () => {
    const response = await fetch('/api/data');
    const data = await response.json();
    expect(data.message).toBe('Hello from mock server');
  });
});

Использование middleware позволяет изолировать тестируемый код от внешнего API, ускоряет тестирование и делает его более предсказуемым.


Настройка при динамическом изменении middleware

Karma поддерживает горячую замену middleware через config.set во время работы сервера. Это полезно для тестирования различных сценариев без перезапуска Karma:

server.on('request', (req, res) => {
  if (req.url === '/toggle') {
    config.set({
      middleware: ['alternateMock']
    });
  }
});

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


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

  1. Разделять middleware по функциональности: логирование, обработка CORS, имитация API.
  2. Всегда вызывать next() для необработанных запросов.
  3. Использовать асинхронные функции аккуратно, гарантируя завершение ответа.
  4. Сохранять порядок подключения middleware, так как Karma выполняет их последовательно.
  5. Хранить сложные middleware в отдельных файлах и подключать через require, чтобы конфигурация оставалась чистой.

Такой подход позволяет создавать надежную и расширяемую инфраструктуру для тестирования HTTP-запросов в приложениях JavaScript.