Зависимости между тестами

Cypress, как инструмент для end-to-end тестирования на JavaScript, ориентирован на изолированное выполнение тестов. Каждое тестовое событие должно быть независимым, чтобы гарантировать воспроизводимость и стабильность тестовой среды. Тем не менее, на практике часто возникает необходимость в организации зависимостей между тестами для более сложных сценариев, особенно при работе с последовательными действиями или данными, которые формируются на предыдущих шагах.


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

Основной принцип Cypress заключается в том, что каждый it блок должен быть самостоятельным. Это означает:

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

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

describe('Проверка авторизации', () => {
  beforeEach(() => {
    cy.visit('/login');
  });

  it('успешный вход с корректными данными', () => {
    cy.get('#username').type('user');
    cy.get('#password').type('password');
    cy.get('#loginButton').click();
    cy.url().should('include', '/dashboard');
  });

  it('неуспешный вход с некорректным паролем', () => {
    cy.get('#username').type('user');
    cy.get('#password').type('wrongpass');
    cy.get('#loginButton').click();
    cy.get('.error-message').should('be.visible');
  });
});

Каждый тест сам создаёт необходимое состояние и проверяет результат независимо.


Причины возникновения зависимостей

Иногда появляется необходимость передавать состояние или данные между тестами. Типичные случаи:

  1. Создание ресурса в первом тесте для проверки в последующих.
  2. Пошаговые сценарии, где каждое действие логически вытекает из предыдущего.
  3. Проверка сложных пользовательских потоков, где невозможно начать с чистого состояния.

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


Способы управления зависимостями

1. Использование before и beforeEach

Блоки before и beforeEach позволяют подготовить тестовую среду до выполнения тестов.

describe('Работа с ресурсами', () => {
  let resourceId;

  before(() => {
    cy.request('POST', '/api/resource', { name: 'Тестовый ресурс' })
      .then((response) => {
        resourceId = response.body.id;
      });
  });

  it('должен получить созданный ресурс', () => {
    cy.request(`/api/resource/${resourceId}`).its('status').should('eq', 200);
  });

  it('должен обновить ресурс', () => {
    cy.request('PUT', `/api/resource/${resourceId}`, { name: 'Обновлённый ресурс' })
      .its('status').should('eq', 200);
  });
});

Ключевой момент: использование глобальной переменной (resourceId) для передачи состояния между тестами позволяет избежать полного дублирования логики создания ресурса.


2. Сохранение состояния через Cypress.env

Cypress.env используется для хранения данных между тестами без обращения к глобальным переменным. Пример:

describe('Авторизация и работа с токеном', () => {
  before(() => {
    cy.request('POST', '/api/login', { user: 'user', pass: 'pass' })
      .then((response) => {
        Cypress.env('authToken', response.body.token);
      });
  });

  it('доступ к защищённому ресурсу', () => {
    cy.request({
      url: '/api/protected',
      headers: { Authorization: `Bearer ${Cypress.env('authToken')}` }
    }).its('status').should('eq', 200);
  });
});

Использование Cypress.env делает тесты более управляемыми и уменьшает риск появления скрытых зависимостей.


3. Последовательное выполнение с помощью it.only или объединение шагов

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

it('создаёт ресурс, редактирует и проверяет результат', () => {
  cy.request('POST', '/api/resource', { name: 'Ресурс' })
    .then((res) => {
      cy.request('PUT', `/api/resource/${res.body.id}`, { name: 'Новый ресурс' });
    })
    .then(() => {
      cy.get(`/resource-page`).should('contain', 'Новый ресурс');
    });
});

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


Риски зависимости тестов

  1. Сложность отладки: если один тест падает, последующие также могут упасть, скрывая истинную причину ошибки.
  2. Снижение стабильности CI/CD: автоматические сборки могут показывать нестабильные результаты.
  3. Нарушение принципа повторяемости: тесты, зависимые от состояния, сложно воспроизводить на чистой среде.

Эти риски подчёркивают, что любые зависимости должны быть осознанными и минимальными, предпочтительно реализуемыми через подготовительные шаги (before, beforeEach) или внешние API.


Рекомендации по проектированию зависимых тестов

  • Минимизировать связи между it блоками.
  • Использовать фикстуры и API-запросы для подготовки данных вместо использования предыдущего теста как источника.
  • Явно очищать состояние после тестов с помощью after или afterEach.
  • Документировать зависимые шаги, чтобы было понятно, почему один тест требует результат другого.

Управление последовательными сценариями

Для сложных бизнес-процессов рекомендуется применять цепочку команд внутри одного теста или использовать страничные объекты (Page Object Pattern) для управления состоянием, что позволяет:

  • Логически разделять шаги без создания отдельных зависимых тестов.
  • Сохранять повторно используемую логику действий и проверок.
  • Уменьшить количество скрытых зависимостей и повысить стабильность тестов.
class LoginPage {
  login(user, pass) {
    cy.get('#username').type(user);
    cy.get('#password').type(pass);
    cy.get('#loginButton').click();
  }
}

describe('Бизнес-процесс', () => {
  const loginPage = new LoginPage();

  it('полный сценарий заказа', () => {
    loginPage.login('user', 'pass');
    cy.visit('/order');
    cy.get('#addItem').click();
    cy.get('#checkout').click();
    cy.get('.confirmation').should('contain', 'Спасибо за заказ');
  });
});

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