Тестирование роутинга

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

Основные концепции роутинга

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

Прежде чем приступить к тестированию роутинга, важно понимать следующие компоненты:

  • Маршрут — это URL, который будет обрабатываться сервером.
  • Обработчик маршрута — это функция, которая выполняется, когда запрос поступает на определённый маршрут.
  • HTTP-методы — методы запросов, такие как GET, POST, PUT, DELETE, каждый из которых имеет разные особенности в контексте роутинга.

Настройка тестового окружения

Для тестирования роутинга часто используется связка Mocha с дополнительными инструментами, такими как Chai и Supertest. Mocha обеспечит запуск тестов и структуру, Chai — ассерты, а Supertest — возможность отправки HTTP-запросов к серверу и проверки результатов.

Пример установки зависимостей:

npm install mocha chai supertest --save-dev

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

const express = require('express');
const app = express();

app.get('/home', (req, res) => {
  res.status(200).send('Welcome to the homepage');
});

module.exports = app;

Теперь, когда сервер готов, можно приступать к тестированию.

Тестирование GET-запросов

Один из самых популярных типов запросов — это GET, который используется для получения данных с сервера. Для тестирования таких запросов с использованием Mocha, Chai и Supertest можно написать следующие тесты.

Пример теста для маршрута /home:

const request = require('supertest');
const app = require('./app');
const { expect } = require('chai');

describe('GET /home', function() {
  it('должен возвращать статус 200 и сообщение "Welcome to the homepage"', function(done) {
    request(app)
      .get('/home')
      .expect(200)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('Welcome to the homepage');
        done();
      });
  });
});

В этом тесте используется метод request(app).get('/home'), который имитирует отправку GET-запроса на сервер. В методе expect(200) проверяется, что сервер вернул статус 200. Затем с помощью Chai делаем проверку тела ответа, ожидая строку “Welcome to the homepage”.

Тестирование POST-запросов

POST-запросы используются для отправки данных на сервер. Чаще всего они применяются для создания новых записей или выполнения операций, требующих данных от клиента. Пример теста для POST-запроса:

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  if (username === 'admin' && password === 'password') {
    return res.status(200).send('Login successful');
  }
  return res.status(401).send('Invalid credentials');
});

describe('POST /login', function() {
  it('должен возвращать статус 200 при правильных учетных данных', function(done) {
    request(app)
      .post('/login')
      .send({ username: 'admin', password: 'password' })
      .expect(200)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('Login successful');
        done();
      });
  });

  it('должен возвращать статус 401 при неверных учетных данных', function(done) {
    request(app)
      .post('/login')
      .send({ username: 'admin', password: 'wrongpassword' })
      .expect(401)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('Invalid credentials');
        done();
      });
  });
});

Здесь мы проверяем два сценария: успешный логин с правильными данными и отказ в случае неверных данных. В обоих тестах используется метод send() для отправки данных в теле запроса.

Тестирование параметров маршрута

Маршруты могут принимать параметры через URL. Например, маршрут для получения информации о пользователе по его идентификатору может выглядеть так:

app.get('/user/:id', (req, res) => {
  const { id } = req.params;
  res.status(200).send(`User ID is ${id}`);
});

Для тестирования таких маршрутов необходимо передать параметр в URL:

describe('GET /user/:id', function() {
  it('должен возвращать правильный ID пользователя', function(done) {
    request(app)
      .get('/user/123')
      .expect(200)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('User ID is 123');
        done();
      });
  });
});

В этом тесте проверяется, что в ответе содержится строка с правильным ID пользователя.

Тестирование ошибок роутинга

Очень важно тестировать ошибки, которые могут возникнуть при обработке запросов. Например, если маршрут не существует, сервер должен вернуть ошибку 404:

app.use((req, res) => {
  res.status(404).send('Not Found');
});

describe('404 ошибка', function() {
  it('должен возвращать статус 404 для несуществующего маршрута', function(done) {
    request(app)
      .get('/non-existent-route')
      .expect(404)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('Not Found');
        done();
      });
  });
});

Здесь проверяется, что сервер корректно обрабатывает запросы к несуществующим маршрутам и возвращает статус 404 с соответствующим сообщением.

Мока и асинхронные тесты

Часто при тестировании роутинга необходимо работать с асинхронными операциями, например, с запросами к базе данных. Mocha предоставляет поддержку асинхронных тестов через использование функции done() или возврат промисов.

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

app.get('/data', (req, res) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      res.status(200).send('Async data');
      resolve();
    }, 1000);
  });
});

describe('GET /data', function() {
  it('должен возвращать данные асинхронно', function() {
    return request(app)
      .get('/data')
      .expect(200)
      .then((res) => {
        expect(res.text).to.equal('Async data');
      });
  });
});

Здесь используется промис для симуляции асинхронной операции. Mocha ожидает завершения промиса, прежде чем продолжить выполнение теста.

Роутинг с использованием middleware

Роутинг часто включает использование промежуточных обработчиков (middleware). Они могут быть использованы для аутентификации, логирования и других целей. Тестирование таких маршрутов требует проверки работы middleware.

Пример теста для маршрута с middleware:

const checkAuth = (req, res, next) => {
  if (req.headers.authorization === 'Bearer validtoken') {
    next();
  } else {
    res.status(401).send('Unauthorized');
  }
};

app.get('/protected', checkAuth, (req, res) => {
  res.status(200).send('Protected data');
});

describe('GET /protected', function() {
  it('должен возвращать статус 200 с правильным токеном', function(done) {
    request(app)
      .get('/protected')
      .set('Authorization', 'Bearer validtoken')
      .expect(200)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('Protected data');
        done();
      });
  });

  it('должен возвращать статус 401 без токена', function(done) {
    request(app)
      .get('/protected')
      .expect(401)
      .end((err, res) => {
        if (err) return done(err);
        expect(res.text).to.equal('Unauthorized');
        done();
      });
  });
});

Здесь проверяется, что middleware корректно защищает маршрут и отказывает в доступе без авторизации.

Заключение

Тестирование роутинга с использованием Mocha позволяет убедиться