Screenplay

Проблема

По мере роста проекта Page Object Model начинает страдать от нарушения принципа единственной ответственности: один Page Object отвечает и за поиск элементов, и за навигацию, и за бизнес-логику, и за ассерты. Классы разрастаются до сотен методов. Тесты становятся процедурными: «нажми это, введи то, проверь результат» — без связи с реальным пользовательским намерением. Повторное использование шагов между тестами затруднено.

Screenplay — это альтернативная архитектура, основанная на трёх концепциях:

  • Actor (Актор) — субъект, выполняющий действия. У актора есть способности (Abilities).
  • Task (Задача) — высокоуровневое действие, описывающее намерение пользователя (SearchFor, LogIn, PlaceOrder). Задача может состоять из других задач.
  • Question (Вопрос) — запрос состояния системы (PageTitle, SearchResults). Вопрос возвращает значение для ассерта.

Тест читается как спецификация поведения: «Актор Иван умеет работать с браузером. Иван выполняет задачу поиска. Иван спрашивает заголовок страницы».

Структура модуля

screenplay/
├── build.gradle
└── src/
    ├── main/
    │   └── java/
    │       └── dev/shipovalov/screenplay/
    │           ├── core/
    │           │   ├── Actor.java             # Актор со способностями, выполняет Tasks и Questions
    │           │   ├── Ability.java           # Интерфейс способности (маркерный)
    │           │   ├── Task.java              # Интерфейс задачи: performAs(Actor)
    │           │   └── Question.java          # Интерфейс вопроса: answeredBy(Actor)
    │           ├── abilities/
    │           │   └── BrowseTheWeb.java      # Способность: владеет WebDriver
    │           ├── tasks/
    │           │   ├── NavigateTo.java        # Задача: открыть URL
    │           │   └── SearchFor.java         # Задача: ввести запрос и выполнить поиск
    │           └── questions/
    │               └── PageTitle.java         # Вопрос: каков заголовок текущей страницы?
    └── test/
        └── java/
            └── dev/shipovalov/screenplay/
                └── ScreenplayPatternTest.java # Тест в стиле «актор выполняет задачи»

Ключевые классы

Класс Назначение
Actor Хранит Map<Class, Ability>, делегирует выполнение в Task.performAs() и Question.answeredBy()
BrowseTheWeb Реализует Ability, оборачивает WebDriver, предоставляет доступ к нему другим компонентам
NavigateTo Реализует Task, открывает URL через BrowseTheWeb.as(actor).getDriver().get(url)
SearchFor Реализует Task, выполняет поиск; внутри может вызывать другие Tasks
PageTitle Реализует Question<String>, возвращает driver.getTitle()

Пример теста

Actor ivan = Actor.named("Иван")
    .whoCan(BrowseTheWeb.with(driver));

ivan.attemptsTo(
    NavigateTo.theUrl("https://google.com"),
    SearchFor.theQuery("Screenplay pattern")
);

String title = ivan.asksAbout(PageTitle.ofCurrentPage());
assertThat(title).contains("Screenplay pattern");

Запуск тестов

./gradlew :screenplay:test

Отчёт о результатах: screenplay/build/reports/tests/test/index.html