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