Оглавление

Введение
Что такое исключения
Иерархия исключений
Обработка исключений
Типы исключений
Заключение

Введение

Когда пишешь автотесты на Java, рано или поздно сталкиваешься с ситуацией: тест упал, в консоли — длинная портянка текста, и непонятно, с чего начать разбираться. Элемент не найден? Переменная оказалась пустой? Или что-то совсем неожиданное?

Все эти падения — результат работы системы исключений (exceptions). Это встроенный механизм Java, который сигнализирует: «Что-то пошло не так, и вот что именно». Понимание того, как устроены исключения, помогает автоматизатору писать тесты, которые корректно реагируют на непредвиденные ситуации и не рушатся при малейшей проблеме. Так же эта тема является популярной на собеседованиях.

Что такое исключения

Исключение (Exception) — это событие, которое нарушает нормальный ход выполнения программы.

Проще говоря, это “сигнал” от JVM: что-то пошло не так во время выполнения кода.

Например:

  • Попытка открыть несуществующий файл → FileNotFoundException

  • Обращение к неинициализированной переменной → NullPointerException

  • Клик по несуществующему элементу → NoSuchElementException

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

Иерархия исключений

Всё начинается с класса Throwable, который является базовым для всех ошибок и исключений в Java. Всё, что может быть «брошено» с помощью throw, наследуется от него.

От Throwable наследуются два больших семейства: Error и Exception. Эти два типа можно условно разделить на системные ошибки, связанные с работой JVM и ресурсов системы, и программные ошибки, которые возникают в ходе выполнения кода и с которыми программа может справиться.

Иерархия исключений
Иерархия исключений

Error — это ошибки, связанные с самой виртуальной машиной Java или системными ресурсами. Обычно их не обрабатывают в коде, потому что они сигнализируют о серьёзных проблемах, от которых программа не сможет восстановиться. Примеры таких ошибок: OutOfMemoryError, StackOverflowError, VirtualMachineError. Для тестировщика важно понимать, что эти ошибки могут возникнуть при работе с большим количеством тестов или больших объёмов данных и их решают на уровне настроек среды, а не через обработку в коде.

Exception — это исключения, которые можно и нужно обрабатывать. Они показывают, что во время работы программы возникла проблема, с которой можно что-то сделать. Исключения делятся на два типа: проверяемые и непроверяемые.

Проверяемые исключения (checked) компилятор требует обязательно обработать или указать в сигнатуре метода. К ним относятся, например, IOException и SQLException. Они чаще всего связаны с внешними ресурсами, такими как файлы, базы данных или сетевые соединения.

Непроверяемые исключения (unchecked) наследуются от RuntimeException. Их можно не ловить, и компилятор не будет ругаться, но они часто приводят к падению программы, если их не предусмотреть. К таким исключениям относятся NullPointerException, IndexOutOfBoundsException и NoSuchElementException. Обычно они возникают из-за ошибок в логике программы или тестов.

Ссылки на официальную документацию Java для исключений

Error (критические ошибки JVM):

RuntimeException (непроверяемые исключения):

Checked Exception (проверяемые исключения):

Обработка исключений

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

Когда в программе возникает исключение, есть два основных варианта действий:

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

  2. Пробросить исключение дальше — передать его выше по стеку вызовов, чтобы обработка произошла в другом месте.

try-catch-finally

  • try — блок кода, в котором может произойти ошибка.

  • catch — блок, который перехватывает конкретное исключение и позволяет с ним работать.

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

Пример:

WebDriver driver = null;
try {
    driver = new ChromeDriver();
    driver.get("https://example.com");
} catch (WebDriverException e) {
    System.out.println("Не удалось запустить браузер: " + e.getMessage());
} finally {
    if (driver != null) {
        driver.quit();
    }
    System.out.println("Блок finally выполняется всегда");
}

Можно использовать несколько блоков catch для разных типов исключений:

try {
    int[] numbers = {1, 2, 3};
    System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Ошибка индекса массива");
} catch (Exception e) { // Важно: более общее исключение должно быть ниже!
    System.out.println("Общее исключение");
}

throw

  • Оператор throw создаёт и «выбрасывает» исключение вручную.

  • Используется, когда метод сталкивается с ситуацией, которую он не может обработать сам.

Пример:

public void checkAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Возраст не может быть отрицательным");
    }
    System.out.println("Возраст корректный");
}

throws

  • С помощью throws метод сообщает, какие исключения он может выбросить.

  • Вызывающий метод должен либо обработать эти исключения, либо тоже указать их через throws.

Пример:

public void readFile() throws IOException {
    FileReader reader = new FileReader("data.txt");
}

try-with-resources

  • Это специальная форма try, которая автоматически закрывает ресурсы (например, файлы, потоки, соединения с базой данных) после завершения блока.

  • Ресурс должен реализовывать интерфейс AutoCloseable.

  • Позволяет не писать отдельный блок finally для освобождения ресурсов — метод close() вызывается автоматически.

Пример:

try (FileReader reader = new FileReader("data.txt");
     BufferedReader bufferedReader = new BufferedReader(reader)) {
    String line = bufferedReader.readLine();
    System.out.println("Первая строка: " + line);
} catch (IOException e) {
    System.out.println("Ошибка при работе с файлом: " + e.getMessage());
}
// reader и bufferedReader автоматически закрыты здесь

Можно объявить несколько ресурсов через точку с запятой — все они будут закрыты автоматически в обратном порядке объявления.

Сравните с подходом без try-with-resources:

FileReader reader = null;
BufferedReader bufferedReader = null;
try {
    reader = new FileReader("data.txt");
    bufferedReader = new BufferedReader(reader);
    String line = bufferedReader.readLine();
    System.out.println("Первая строка: " + line);
} catch (IOException e) {
    System.out.println("Ошибка при работе с файлом: " + e.getMessage());
} finally {
    try {
        if (bufferedReader != null) bufferedReader.close();
        if (reader != null) reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Как видите, try-with-resources делает код намного чище и безопаснее.

Проброс исключения

Иногда метод не может обработать ошибку сам и передаёт её выше по стеку вызовов с помощью throws. Это называется пробросом исключения.

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

Пример с цепочкой из трёх методов:

public void readFile() throws IOException {
    FileReader reader = new FileReader("data.txt"); // может выбросить IOException
}

public void processFile() throws IOException {
    readFile(); // вызываем метод, который может выбросить исключение
}

public void runTest() {
    try {
        processFile(); // вызываем верхний метод цепочки
    } catch (IOException e) {
        System.out.println("Не удалось прочитать файл: " + e.getMessage());
    }
}

В этом примере:

  1. readFile() — сам не обрабатывает исключение, указывает через throws, что оно может произойти.

  2. processFile() — также не обрабатывает, а пробрасывает дальше.

  3. runTest() — перехватывает исключение и решает, что с ним делать.

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

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

Типы исключений

RuntimeException (непроверяемые исключения)

Исключение

Что значит

NullPointerException

Попытка обратиться к null

IndexOutOfBoundsException

Выход за пределы массива или списка

NoSuchElementException

Элемент не найден

IllegalArgumentException

Метод получил неверный аргумент

ArithmeticException

Деление на ноль или другие арифметические ошибки

IllegalStateException

Метод вызван в неправильном состоянии объекта

ClassCastException

Неверное приведение объекта к другому типу

UnsupportedOperationException

Операция не поддерживается

ConcurrentModificationException

Коллекция была изменена во время итерации

NumberFormatException

Неверный формат числа

Checked Exception (проверяемые исключения)

Исключение

Что значит

IOException

Ошибка ввода/вывода

FileNotFoundException

Файл не найден

SQLException

Ошибка запроса к базе данных

ClassNotFoundException

Класс не найден в classpath

InterruptedException

Поток был прерван

ParseException

Ошибка разбора формата

MalformedURLException

Некорректный URL

ConnectException

Ошибка подключения

EOFException

Неожиданный конец файла или потока

Error (критические ошибки JVM)

Исключение

Что значит

OutOfMemoryError

Закончилась память

StackOverflowError

Переполнение стека

VirtualMachineError

Ошибка виртуальной машины

InternalError

Внутренняя ошибка JVM

UnknownError

Неизвестная критическая ошибка

LinkageError

Конфликт версий классов или библиотек

AssertionError

Проверка (assert) не прошла

Исключения Selenium WebDriver (непроверяемые)

Исключение

Что значит

NoSuchElementException

Элемент не найден на странице

StaleElementReferenceException

Элемент был обновлён или исчез со страницы

ElementNotInteractableException

Элемент не доступен для взаимодействия (не кликабелен)

TimeoutException

Элемент не появился за отведённое время

WebDriverException

Общая ошибка WebDriver

NoSuchWindowException

Окно или вкладка браузера не найдены

NoSuchFrameException

Фрейм не найден на странице

InvalidSelectorException

Некорректный CSS или XPath селектор

ElementClickInterceptedException

Клик перехвачен другим элементом (например, модалкой)

NoAlertPresentException

Alert отсутствует на странице

Примечание: Все исключения Selenium WebDriver наследуются от RuntimeException, поэтому их можно не обрабатывать явно, но в автотестах рекомендуется их перехватывать для более информативного логирования и корректной обработки ошибок.

Заключение

Исключения в Java — сигнализируют о проблемах в коде или окружении, помогают быстрее находить баги и принимать правильные решения. Понимая, как читать, обрабатывать и пробрасывать исключения, вы сможете писать более надёжные и устойчивые автотесты, которые не падают из-за непредвиденных ситуаций.

Комментарии (0)