Что такое принципы SOLID?

Ответ

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

  1. S — Single Responsibility (Принцип единственной ответственности).

    • Суть: Класс или модуль должен иметь одну и только одну причину для изменения (одну ответственность).
    • Применение в автотестах: Разделяйте классы для работы с страницами (Page Object), классы для тестовых данных, классы для утилитарных функций (например, генерации данных) и классы для работы с API. Не стоит смешивать логику проверок (assertions) и логику взаимодействия с браузером в одном методе.
  2. O — Open/Closed (Принцип открытости/закрытости).

    • Суть: Программные сущности должны быть открыты для расширения, но закрыты для модификации.
    • Применение в автотестах: Создавайте базовые классы или интерфейсы для основных действий (например, BaseTest с настройкой драйвера), а затем расширяйте их для конкретных тест-сьютов, не переписывая базовую логику. Использование паттерна Стратегия для выбора браузера или окружения — хороший пример.
  3. L — Liskov Substitution (Принцип подстановки Барбары Лисков).

    • Суть: Объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности программы.
    • Применение в автотестах: Если у вас есть базовый класс WebPage с методом isLoaded(), то любой класс, его наследующий (например, LoginPage, HomePage), должен корректно работать везде, где ожидается WebPage, не ломая логику проверки загрузки страницы.
  4. I — Interface Segregation (Принцип разделения интерфейсов).

    • Суть: Много специализированных интерфейсов лучше, чем один универсальный.
    • Применение в автотестах: Вместо одного громоздкого интерфейса TestActions, который включает методы для UI, API и работы с БД, лучше создать отдельные интерфейсы: UiActions, ApiClient, DbHelper. Тогда класс, который работает только с API, не будет вынужден реализовывать ненужные ему UI-методы.
  5. D — Dependency Inversion (Принцип инверсии зависимостей).

    • Суть: Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
    • Применение в автотестах: Класс теста не должен напрямую создавать экземпляр конкретного драйвера (ChromeDriver). Вместо этого он должен зависеть от абстракции WebDriver. Конкретная реализация драйвера (Chrome, Firefox) «инжектируется» (например, через конструктор) извне, что упрощает поддержку и тестирование.

Практическая польза для QA: Следование SOLID в коде автотестов снижает сцепление (coupling) и повышает связность (cohesion), что ведёт к уменьшению количества багов в самих тестах, упрощению их рефакторинга и увеличению скорости написания новых тестовых сценариев.

Ответ 18+ 🔞

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

Представь, что твой автотестовый фреймворк — это твоя тачка. SOLID — это правила, чтобы она не развалилась на первой кочке и чтобы тебе не пришлось весь двигатель снимать, чтобы лампочку поменять.

S — Single Responsibility (Единственная ответственность). Это как раз про ту самую лампочку. Суть проще пареной репы: один модуль — одна работа. Не надо делать класс-универсал, который и в базу лезет, и кнопки жмёт, и отчёты красивые рисует. Это пиздец как неправильно. Сделай отдельный класс LoginPage — его дело только авторизацию на странице отрабатывать. Отдельный DataGenerator — пусть себе тестовые данные плодит. А ApiHelper пусть болтается с API. И тогда, когда в API что-то поменяется, тебе не придётся перелопачивать половину кода, а только в одном месте ковыряться. Доверия ебать ноль к этим мега-классам, которые за всё отвечают.

O — Open/Closed (Открыт для расширения, закрыт для изменений). Вот смотри. У тебя есть базовый класс BaseTest, где у тебя вся подготовка: драйвер запустить, куки почистить. Это твоя основа, она работает и трогать её страшно. И тут тебе надо тесты под мобилку добавить. Ты что, полезешь в BaseTest и начнёшь там if-else городить? Хуй с горы! Ты просто создашь новый класс MobileTest, который унаследуется от BaseTest, и в нём переопределишь только настройку драйвера под андроид. Старый код не тронут, новый функционал добавлен. Красота, ёпта.

L — Liskov Substitution (Подстановка Лисков). Звучит страшно, а смысл — проще некуда. Если у тебя есть, условно, класс WebPage с методом open(), и от него наследуются LoginPage и MainPage, то в любом месте кода, где ты используешь WebPage, ты должен спокойно можешь подсунуть LoginPage, и всё будет работать. Не должно быть такого, что ты подставил наследника, а у него метод open() не открывает страницу, а, например, начинает музыку играть. Это будет полный пидарас шерстяной дизайн. Если наследник ломает логику родителя — это нарушение принципа, и терпения ноль ебать с таким кодом.

I — Interface Segregation (Разделение интерфейсов). Это про то, чтобы не делать из интерфейса швейцарский нож. Не надо городить интерфейс TestActions на 150 методов, где есть и clickButton(), и sendApiRequest(), и queryDatabase(). Потом классу, который только API тестирует, придётся реализовывать кучу пустых или заглушечных методов для UI. Это же манда с ушами! Гораздо лучше наклепать несколько узких интерфейсов: UiInteractor, ApiClient, DbConnector. И тогда каждый класс берёт только то, что ему нужно. Чисто, опрятно.

D — Dependency Inversion (Инверсия зависимостей). Самый, пожалуй, важный для гибкости. Верхнеуровневые модули (твои тесты) не должны жёстко зависеть от нижнеуровневых (конкретная реализация драйвера). Вот смотри на эту дичь:

// ПЛОХО: тест жёстко завязан на хром
class BadTest {
    WebDriver driver = new ChromeDriver();
    // ... тест использует driver
}

А если надо на фаерфоксе прогнать? Переписывать? Ебать копать! Делаем по-взрослому:

// ХОРОШО: тест зависит от абстракции WebDriver
class GoodTest {
    WebDriver driver;
    // Драйвер "внедряется" извне (через конструктор)
    GoodTest(WebDriver driver) {
        this.driver = driver;
    }
}

Конкретку — ChromeDriver или FirefoxDriver — создаёт и подсовывает тесту специальная фабрика или фреймворк (типа TestNG или JUnit). Тест же знает только абстракцию. Хоть хуй в пальто подсуни — лишь бы интерфейс WebDriver реализовывал. Теперь сменить браузер — раз плюнуть.

Итог для QA-инженера: Если применять эти принципы, код автотестов перестаёт быть грудой спагетти, где всё связано со всем. Он становится модульным. Падает сцепление (одна поломка не валит всё), растёт связность (всё related лежит вместе). Новый сценарий пишешь быстрее, потому что не изобретаешь велосипед, а используешь готовые, отлаженные кирпичики. А когда в приложении что-то меняется, правки в тестах вносить не овердохуища работы, а точечно и понятно. В общем, вы ходите по охуенно тонкому льду, если этим пренебрегаете.

Видео-ответы