Логирование ошибок

Логирование ошибок в Protractor играет ключевую роль при построении надёжных e2e-тестов для Angular-приложений. При росте тестового набора и усложнении инфраструктуры простого вывода stack trace становится недостаточно — требуется структурированная, воспроизводимая и анализируемая система логирования.


Ошибки в Protractor можно условно разделить на несколько категорий:

Ошибки Selenium WebDriver

Возникают на уровне взаимодействия с браузером:

  • NoSuchElementError
  • StaleElementReferenceError
  • TimeoutError
  • ошибки сессии браузера

Ошибки синхронизации с Angular

Проявляются при:

  • неправильной конфигурации waitForAngular
  • использовании сторонних библиотек, не отслеживаемых Zone.js
  • асинхронных операций вне Angular-контекста

Ошибки тестовой логики

Связаны с:

  • некорректными ожиданиями
  • неверными селекторами
  • ошибками в page object’ах
  • неправильной обработкой промисов

Каждый тип ошибки требует разного уровня детализации в логах.


Стандартный механизм вывода ошибок

По умолчанию Protractor использует Jasmine или Mocha как тестовый фреймворк. Ошибки выводятся в консоль в виде:

  • сообщения об ошибке
  • stack trace
  • информации о тесте (suite/spec)

Такой вывод ограничен:

  • отсутствует контекст браузера
  • нет состояния приложения
  • сложно агрегировать данные при CI-запусках

Использование логирования через browser.manage().logs()

WebDriver позволяет получать логи браузера:

browser.manage().logs().get('browser').then(function(logs) {
  logs.forEach(function(log) {
    console.log(log.message);
  });
});

Типы логов:

  • browserconsole.log, console.error
  • driver — внутренние логи WebDriver
  • performance — сетевые события

Особенно ценны console.error, так как позволяют выявлять JavaScript-ошибки приложения, не приводящие к падению теста напрямую.


Перехват ошибок в хуках Jasmine

afterEach для сбора информации

afterEach(async function () {
  const logs = await browser.manage().logs().get('browser');
  logs
    .filter(log => log.level.name === 'SEVERE')
    .forEach(log => console.error(log.message));
});

Преимущества:

  • логирование происходит только при ошибках
  • фиксируется состояние браузера после теста
  • удобно комбинировать со скриншотами

Скриншоты как часть логирования

Скриншот является важным визуальным логом.

afterEach(async function () {
  const spec = jasmine.getEnv().currentSpec;
  if (spec.result.failedExpectations.length > 0) {
    const png = await browser.takeScreenshot();
    // сохранение в файл
  }
});

Практика:

  • сохранять имя spec’а в названии файла
  • добавлять timestamp
  • группировать по suite’ам

Логирование через собственный logger

Использование console.log не масштабируется. Чаще применяется библиотека логирования.

Пример с winston

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

Применение в тестах:

try {
  await element(by.css('.submit')).click();
} catch (e) {
  logger.error('Ошибка клика по кнопке submit', {
    error: e.message,
    stack: e.stack
  });
  throw e;
}

Преимущества:

  • структурированные логи
  • интеграция с CI и ELK
  • фильтрация по уровням

Логирование внутри Page Object

Page Object — ключевая точка логирования ошибок взаимодействия.

class LoginPage {
  async login(user, pass) {
    try {
      await this.username.sendKeys(user);
      await this.password.sendKeys(pass);
      await this.submit.click();
    } catch (e) {
      logger.error('Ошибка логина', {
        user,
        error: e.message
      });
      throw e;
    }
  }
}

Подход позволяет:

  • локализовать ошибки
  • избежать дублирования логики
  • повысить читаемость тестов

Логирование ожиданий и таймаутов

Ожидания — частый источник нестабильности.

await browser.wait(
  EC.visibilityOf(el),
  5000,
  'Элемент не стал видимым'
);

При логировании:

  • фиксируется селектор
  • указывается таймаут
  • сохраняется текущее URL
catch (e) {
  logger.warn('Timeout ожидания элемента', {
    selector: '.modal',
    url: await browser.getCurrentUrl()
  });
  throw e;
}

Централизованная обработка ошибок

Создание утилиты-обёртки:

async function safeStep(description, fn) {
  try {
    await fn();
  } catch (e) {
    logger.error(description, {
      error: e.message,
      stack: e.stack
    });
    throw e;
  }
}

Использование:

await safeStep('Открытие страницы логина', async () => {
  await browser.get('/login');
});

Подход формирует единый стиль логов по всему проекту.


Интеграция с CI/CD

При запуске в CI:

  • логи сохраняются как artifacts
  • скриншоты прикладываются к отчётам
  • JSON-логи парсятся системами мониторинга

Рекомендуемая структура:

logs/
  ├─ browser.log
  ├─ errors.log
screenshots/
  ├─ login_spec_2024-01-12.png

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

При использовании Allure или аналогов:

  • лог-сообщения прикрепляются к шагам
  • ошибки браузера отображаются в отчёте
  • скриншоты автоматически ассоциируются с падением

Это превращает логирование из вспомогательного инструмента в полноценный диагностический механизм.


Практические принципы

  • Логировать не только факт ошибки, но и контекст
  • Не дублировать stack trace без необходимости
  • Разделять уровни: info, warn, error
  • Исключать чувствительные данные
  • Поддерживать единый формат логов

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