Хук beforeEach

beforeEach — это один из хуков, предоставляемых библиотекой Mocha для тестирования. Он позволяет заранее выполнить определённые действия до каждого теста в блоке или на уровне всего файла с тестами. Хук может быть полезен в тех случаях, когда необходимо подготовить общие условия для тестов, например, инициализировать какие-либо данные, настроить окружение или мокать зависимости.

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

Пример использования хука:

const assert = require('assert');

let sum = 0;

beforeEach(function() {
  sum = 0;  // Инициализация переменной перед каждым тестом
});

describe('Тестирование суммы', function() {
  it('должен быть равен нулю перед тестом', function() {
    assert.strictEqual(sum, 0);
  });

  it('должен увеличиваться на 1', function() {
    sum += 1;
    assert.strictEqual(sum, 1);
  });

  it('должен увеличиваться на 2', function() {
    sum += 2;
    assert.strictEqual(sum, 2);
  });
});

В данном примере, хук beforeEach гарантирует, что перед каждым тестом переменная sum будет сброшена на 0. Это делает тесты независимыми друг от друга, что важно для обеспечения точности результатов.

Синхронный и асинхронный beforeEach

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

Синхронный хук:

beforeEach(function() {
  // Здесь выполняются синхронные операции, например:
  this.someState = 0;
});

Асинхронный хук с промисами:

beforeEach(function() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      this.someState = 42;
      resolve();
    }, 1000);
  });
});

Асинхронный хук с async/await:

beforeEach(async function() {
  // Ожидаем завершения асинхронной операции перед каждым тестом
  this.someState = await fetchDataFromServer();
});

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

Хуки внутри describe

Если хук beforeEach используется внутри блока describe, то он будет действовать только для тестов в рамках этого блока. Это позволяет создавать более точечные настройки, где каждый набор тестов может иметь свои собственные предустановленные условия.

Пример:

describe('Тесты для группы A', function() {
  beforeEach(function() {
    this.group = 'A';
  });

  it('тест 1 для группы A', function() {
    assert.strictEqual(this.group, 'A');
  });

  it('тест 2 для группы A', function() {
    assert.strictEqual(this.group, 'A');
  });
});

describe('Тесты для группы B', function() {
  beforeEach(function() {
    this.group = 'B';
  });

  it('тест 1 для группы B', function() {
    assert.strictEqual(this.group, 'B');
  });

  it('тест 2 для группы B', function() {
    assert.strictEqual(this.group, 'B');
  });
});

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

Хуки beforeEach и afterEach

Хук beforeEach часто используется вместе с хук afterEach, который выполняется после каждого теста. Использование этих хуков помогает поддерживать чистоту тестовой среды и контролировать состояние перед и после каждого теста.

Пример с afterEach:

beforeEach(function() {
  this.counter = 0;
});

afterEach(function() {
  console.log('Тест завершён');
});

describe('Тестирование счётчика', function() {
  it('должен увеличиваться на 1', function() {
    this.counter += 1;
    assert.strictEqual(this.counter, 1);
  });

  it('должен увеличиваться на 2', function() {
    this.counter += 2;
    assert.strictEqual(this.counter, 2);
  });
});

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

Важные особенности

  1. Изоляция тестов: Хук beforeEach используется для подготовки изолированной среды для каждого теста. Это позволяет избежать зависимости тестов друг от друга.
  2. Асинхронность: При работе с асинхронными операциями важно помнить о правильной обработке промисов или использовании async/await, чтобы избежать ошибок, связанных с неправильным порядком выполнения.
  3. Использование в блоках describe: Хук beforeEach можно использовать внутри каждого блока describe, что позволяет задавать отдельные условия для разных групп тестов.
  4. Параллельное выполнение тестов: Mocha позволяет запускать тесты параллельно, но это возможно только при правильном использовании хуков beforeEach и afterEach для обеспечения независимости тестов.

Практическое применение

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

Пример тестирования API:

const request = require('supertest');
let server;

beforeEach(function() {
  server = require('../app');  // Запуск сервера перед каждым тестом
});

afterEach(function() {
  server.close();  // Закрытие сервера после каждого теста
});

describe('Тестирование API', function() {
  it('должен возвращать статус 200 для GET /users', function(done) {
    request(server)
      .get('/users')
      .expect(200, done);
  });

  it('должен создавать нового пользователя', function(done) {
    request(server)
      .post('/users')
      .send({ name: 'John' })
      .expect(201, done);
  });
});

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

Заключение

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