Custom error классы

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

Основы создания пользовательских классов ошибок

В JavaScript для создания пользовательских классов ошибок используется механизм наследования от встроенного класса Error. При этом важно учитывать, что любой пользовательский класс ошибки должен быть подклассом Error, чтобы он корректно работал с механизмами обработки исключений (например, try-catch).

Пример создания базового пользовательского класса ошибки:

class CustomError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name; // Название ошибки совпадает с названием класса
    Error.captureStackTrace(this, this.constructor);
  }
}

Здесь класс CustomError расширяет функциональность стандартного класса Error, добавляя дополнительные свойства или методы при необходимости. Важно, что для корректного отображения стека вызовов используется метод Error.captureStackTrace, который позволяет сохранить стек на момент создания ошибки.

Расширение пользовательских классов ошибок

Для более детальной и структурированной обработки ошибок можно создавать несколько различных классов ошибок, каждый из которых будет предназначен для определенной категории исключений. Например, для тестирования UI-элементов можно создать отдельные классы ошибок для различных типов проблем: ElementNotFoundError, TimeoutError, AssertionFailedError.

Пример:

class ElementNotFoundError extends CustomError {
  constructor(element) {
    super(`Элемент ${element} не найден на странице`);
    this.element = element;
    this.statusCode = 404;
  }
}

class TimeoutError extends CustomError {
  constructor(timeout) {
    super(`Таймаут: ожидание элемента прошло больше чем ${timeout} мс`);
    this.timeout = timeout;
    this.statusCode = 408;
  }
}

class AssertionFailedError extends CustomError {
  constructor(assertion) {
    super(`Проверка не прошла: ${assertion}`);
    this.assertion = assertion;
    this.statusCode = 400;
  }
}

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

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

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

Пример обработки ошибок:

try {
  const element = await $('#element');
  if (!element) {
    throw new ElementNotFoundError('#element');
  }
  await element.click();
} catch (error) {
  if (error instanceof ElementNotFoundError) {
    console.error(error.message);
    // Дополнительные действия, например, скриншот или запись лога
  } else {
    throw error; // Прокидываем другие ошибки дальше
  }
}

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

Логирование и отчетность ошибок

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

Пример интеграции с системой логирования:

try {
  // Логика теста
} catch (error) {
  if (error instanceof CustomError) {
    logToMonitoringSystem({
      error: error.name,
      message: error.message,
      stack: error.stack,
      statusCode: error.statusCode
    });
  }
  throw error; // Прокидываем ошибку для дальнейшей обработки
}

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

Важные моменты при создании классов ошибок

  1. Использование стеков вызовов: Метод Error.captureStackTrace помогает сохранить стек вызовов, что позволяет более точно отследить, где произошла ошибка.

  2. Четкость сообщений: Каждое сообщение ошибки должно быть четким и информативным. Указывайте, что именно пошло не так и при необходимости — контекст.

  3. Гибкость и расширяемость: Используйте наследование для создания множества типов ошибок. Это позволит легко расширять систему обработки ошибок при изменении требований к проекту.

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

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

Заключение

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