Независимость тестов от данных

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

Проблемы, связанные с зависимостью от данных

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

  • Нестабильность тестов. Если тесты зависят от данных, которые могут изменяться во время выполнения других тестов или в процессе работы приложения, это может привести к непредсказуемым результатам и трудностям при отладке.
  • Сложность воспроизведения ошибок. Когда тесты зависят от динамических данных, проблемы, выявленные на одном тесте, могут не повторяться на других тестах из-за изменений в данных. Это затрудняет диагностику и исправление ошибок.
  • Проблемы с производительностью. Тесты, зависимые от внешних данных, требуют большего времени на выполнение из-за необходимости встраивания операций с базой данных, загрузки внешних сервисов или сетевых запросов.

Для того чтобы избежать этих проблем, необходимо придерживаться принципов, обеспечивающих независимость тестов от данных.

Стратегии обеспечения независимости тестов от данных

Использование моков и стабов

Моки (mocks) и стабы (stubs) — это искусственные объекты, которые заменяют реальные зависимости в тестах. Они позволяют контролировать ответы, которые тестируемое приложение получает от внешних сервисов или базы данных. Мокирование внешних сервисов и API помогает изолировать тесты от реальных данных и делает их воспроизводимыми и независимыми.

  • Моки: имитируют работу внешних сервисов или API, возвращая заранее заданные данные. Это позволяет тестировать логику приложения без обращения к реальному серверу или базе данных.
  • Стабы: это более простая форма моков, которые имитируют поведение функции или метода, но не обязательно проверяют все взаимодействия с тестируемым объектом.

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

import { test, expect } from '@playwright/test';

test('Тест с моком API', async ({ page }) => {
  await page.route('https://api.example.com/data', (route) => {
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ key: 'value' })
    });
  });

  await page.goto('https://example.com');
  const result = await page.textContent('.result');
  expect(result).toBe('value');
});

Применение базы данных для тестов

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

Это можно сделать через механизмы, такие как:

  • Создание и использование тестовой базы данных: Создание новой базы данных, которая используется только для тестов.
  • Подготовка и очистка данных: Перед каждым тестом устанавливается определенное состояние базы данных, а после выполнения теста данные сбрасываются или возвращаются в исходное состояние.

Пример с использованием тестовой базы данных:

import { test, expect } from '@playwright/test';
import { resetTestDatabase, seedTestDatabase } from './database-utils';

test.beforeEach(async () => {
  await resetTestDatabase();
  await seedTestDatabase();
});

test('Проверка правильности отображения данных', async ({ page }) => {
  await page.goto('https://example.com/data');
  const result = await page.textContent('.data-list');
  expect(result).toBe('Expected Data');
});

Использование фикстур

Фикстуры — это механизмы, которые обеспечивают подготовку и очистку данных или окружения перед каждым тестом. В Playwright можно использовать фикстуры для управления состоянием тестируемого приложения, загрузки тестовых данных и очистки после выполнения теста.

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

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

import { test, expect } from '@playwright/test';
import { createTestUser, deleteTestUser } from './user-utils';

test.beforeEach(async () => {
  const user = await createTestUser();
  test.info().testData = user;
});

test.afterEach(async () => {
  await deleteTestUser(test.info().testData.id);
});

test('Тест с фикстурой', async ({ page }) => {
  const user = test.info().testData;
  await page.goto(`/profile/${user.id}`);
  const profileName = await page.textContent('.username');
  expect(profileName).toBe(user.username);
});

Модульное тестирование и изоляция

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

Пример модульного теста в Playwright:

import { test, expect } from '@playwright/test';
import { mockApiService } from './mock-api';

test('Модульный тест с подменой API', async ({ page }) => {
  mockApiService({ response: { data: 'Test Data' } });

  await page.goto('/home');
  const result = await page.textContent('.api-result');
  expect(result).toBe('Test Data');
});

Устранение зависимости от времени и состояния

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

  • Заморозка времени: для тестов, которые зависят от текущего времени, можно использовать инструменты для заморозки времени (например, с помощью библиотеки mockdate), чтобы гарантировать предсказуемость выполнения тестов.
  • Изоляция состояния сессии и cookies: можно управлять состоянием сессий и cookies для каждого теста, чтобы исключить влияние предыдущих тестов.

Пример заморозки времени:

import { test, expect } from '@playwright/test';
import mockdate from 'mockdate';

test('Тест с замороженным временем', async ({ page }) => {
  mockdate.set('2023-01-01T00:00:00Z');

  await page.goto('/time');
  const currentTime = await page.textContent('.current-time');
  expect(currentTime).toBe('2023-01-01T00:00:00Z');
});

Управление зависимыми внешними сервисами

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

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

import { test, expect } from '@playwright/test';

test('Мокирование ответа от внешнего сервиса', async ({ page }) => {
  await page.route('https://api.example.com/items', (route) => {
    route.fulfill({
      status: 200,
      body: JSON.stringify([{ id: 1, name: 'Test Item' }])
    });
  });

  await page.goto('/items');
  const itemName = await page.textContent('.item-name');
  expect(itemName).toBe('Test Item');
});

Заключение

Независимость тестов от данных является неотъемлемой частью качественного тестирования с Playwright. Использование моков, фикстур, тестовых баз данных и других методов позволяет изолировать тесты от внешних факторов, таких как данные, состояние системы и время. Это способствует более стабильному и предсказуемому выполнению тестов, улучшая их надежность и снижая вероятность ошибок.