Перейти к основному содержимому

Serenity BDD и сценарий

· 6 мин. чтения

1. Обзор

В этой статье мы кратко рассмотрим шаблон сценария в Serenity BDD. Мы предлагаем вам сначала прочитать основы Serenity BDD, прежде чем читать это. Также может быть интересна статья об интеграции Serenity BDD со Spring .

Сценарий, представленный в Serenity BDD, направлен на поощрение хороших привычек тестирования и хорошо разработанных наборов тестов, позволяя командам писать более надежные и надежные тесты. Он основан на Selenium WebDriver и модели Page Objects. Если вы читали наше введение в Selenium , вы найдете эти концепции довольно знакомыми.

2. Зависимость от Maven

Во-первых, добавим в файл pom.xml следующие зависимости :

<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-junit</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-webdriver</artifactId>
<version>1.4.0</version>
</dependency>

Последние версии serenity-screenplay и serenity-screenplay-webdriver можно загрузить из центрального репозитория Maven.

Нам также нужны веб-драйверы для выполнения сценариев — подойдет ChromeDriver или Mozilla-GeckoDriver . В этой статье мы будем использовать ChromeDriver.

Для включения WebDriver требуется следующая конфигурация плагина, в которой значение webdriver.chrome.driver должно быть относительным путем к двоичному файлу ChromeDriver в нашем проекте maven:

<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.20</version>
<configuration>
<systemProperties>
<webdriver.chrome.driver>chromedriver</webdriver.chrome.driver>
</systemProperties>
</configuration>
</plugin>

3. Поддержка веб-драйвера

Мы можем заставить Serenity управлять экземпляром WebDriver, пометив аннотацию @Managed в переменной WebDriver. Serenity открывает соответствующий драйвер в начале каждого теста и выключает его по завершении теста.

В следующем примере мы инициируем ChromeDriver и открываем Google для поиска «foreach». Мы ожидаем, что имя Евгения будет присутствовать в результатах поиска:

@RunWith(SerenityRunner.class)
public class GoogleSearchLiveTest {

@Managed(driver = "chrome")
private WebDriver browser;

@Test
public void whenGoogleForEachThenShouldSeeForEach() {
browser.get("https://www.google.com/ncr");

browser
.findElement(By.name("q"))
.sendKeys("foreach", Keys.ENTER);

new WebDriverWait(browser, 5)https://www.foreach.com/serenity-screenplay
.until(visibilityOfElementLocated(By.cssSelector("._ksh")));

assertThat(browser
.findElement(By.cssSelector("._ksh"))
.getText(), containsString("ForEach (ForEach)"));
}
}

Если мы не укажем никаких параметров для @Managed , Serenity BDD в этом случае будет использовать Firefox. Весь список поддерживаемых драйверов по аннотации @Managed : firefox, chrome, iexplorer, htmlunit, phantomjs .

Если нам нужно протестировать в IExplorer или Edge, мы можем скачать веб-драйверы отсюда (для IE) и отсюда (для Edge) соответственно. Safari WebDriver доступен только в MacOS в папке /usr/bin/safaridriver .

4. Объекты страницы

Объекты страницы Serenity представляют собой объект страницы WebDriver. PageObject скрывает детали WebDriver для повторного использования .

4.1. Пример рефакторинга с использованием PageObject

Давайте сначала уточним наш предыдущий тест с использованием PageObject , извлекая действия по обнаружению элементов, поиску и проверке результатов:

@DefaultUrl("https://www.google.com/ncr")
public class GoogleSearchPageObject extends PageObject {

@FindBy(name = "q")
private WebElement search;

@FindBy(css = "._ksh")
private WebElement result;

public void searchFor(String keyword) {
search.sendKeys(keyword, Keys.ENTER);
}

public void resultMatches(String expected) {
assertThat(result.getText(), containsString(expected));
}
}

WebElement представляет элемент HTML. Мы можем взаимодействовать с веб-страницами через API интерфейса. В приведенном выше примере мы использовали два способа поиска веб-элементов на странице: по имени элемента и по классам CSS элемента.

Существует больше подходов к применению при поиске веб-элементов, таких как поиск по имени тега, поиск по тексту ссылки и т. д. Подробную информацию см. в нашем руководстве по Selenium .

Мы также можем заменить WebElement на WebElementFacade , который предоставляет более удобные API для работы с веб-элементами.

Поскольку Serenity автоматически создаст экземпляры любых полей PageObject в тесте JUnit , предыдущий тест можно переписать, сделав его намного чище:

@RunWith(SerenityRunner.class)
public class GoogleSearchPageObjectLiveTest {

@Managed(driver = "chrome")
private WebDriver browser;

GoogleSearchPageObject googleSearch;

@Test
public void whenGoogleForEachThenShouldSeeForEach() {
googleSearch.open();

googleSearch.searchFor("foreach");

googleSearch.resultMatches("ForEach (ForEach)");
}
}

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

4.2. Асинхронная поддержка

В настоящее время многие веб-страницы обслуживаются или отображаются динамически. Чтобы иметь дело с такими случаями, PageObject также поддерживает множество богатых функций, которые позволяют нам проверять статусы элементов. Мы можем проверить, видны ли элементы, или подождать, пока они не станут видимыми, прежде чем продолжить.

Давайте улучшим метод resultMatches , обеспечив видимость элемента, который мы хотим видеть:

public void resultMatches(String expected) {
waitFor(result).waitUntilVisible();
assertThat(result.getText(), containsString(expected));
}

Если мы не ожидаем слишком долгого ожидания, мы можем явно указать тайм-аут для ожидающих действий:

public void resultMatches(String expected) {
withTimeoutOf(5, SECONDS)
.waitFor(result)
.waitUntilVisible();
assertThat(result.getText(), containsString(expected));
}

5. Шаблон сценария

Шаблон сценария применяет принципы проектирования SOLID к автоматизированному приемочному тестированию. Общее понимание паттерна сценария можно объяснить в контексте данного_когда_тогда следующим образом:

  • данныйАктер , способный выполнить какое-либо Задание
  • когдаАктер выполняет Задание
  • затем — Актер должен увидеть эффект и проверить результаты

Теперь давайте поместим наш предыдущий тестовый сценарий в шаблон сценария: если у пользователя Китти, который может использовать Google, когда она ищет «foreach» в Google, то Китти должна увидеть имя Евгения в результатах.

Во-первых, определите задачи, которые может выполнять Китти.

  1. Китти может использовать Google:
public class StartWith implements Task {

public static StartWith googleSearchPage() {
return instrumented(StartWith.class);
}

GoogleSearchPage googleSearchPage;

@Step("{0} starts a google search")
public <T extends Actor> void performAs(T t) {
t.attemptsTo(Open
.browserOn()
.the(googleSearchPage));
}
}
  1. Китти может выполнить поиск в Google:
public class SearchForKeyword implements Task {

@Step("{0} searches for '#keyword'")
public <T extends Actor> void performAs(T actor) {
actor.attemptsTo(Enter
.theValue(keyword)
.into(GoogleSearchPage.SEARCH_INPUT_BOX)
.thenHit(Keys.RETURN));
}

private String keyword;

public SearchForKeyword(String keyword) {
this.keyword = keyword;
}

public static Task of(String keyword) {
return Instrumented
.instanceOf(SearchForKeyword.class)
.withProperties(keyword);
}
}
  1. Китти может видеть результаты поиска Google:
public class GoogleSearchResults implements Question<List<String>> {

public static Question<List<String>> displayed() {
return new GoogleSearchResults();
}

public List<String> answeredBy(Actor actor) {
return Text
.of(GoogleSearchPage.SEARCH_RESULT_TITLES)
.viewedBy(actor)
.asList();
}
}

Кроме того, мы уже определили PageObject для поиска Google :

@DefaultUrl("https://www.google.com/ncr")
public class GoogleSearchPage extends PageObject {

public static final Target SEARCH_RESULT_TITLES = Target
.the("search results")
.locatedBy("._ksh");

public static final Target SEARCH_INPUT_BOX = Target
.the("search input box")
.locatedBy("#lst-ib");
}

Теперь наш основной тестовый класс будет выглядеть так:

@RunWith(SerenityRunner.class)
public class GoogleSearchScreenplayLiveTest {

@Managed(driver = "chrome")
WebDriver browser;

Actor kitty = Actor.named("kitty");

@Before
public void setup() {
kitty.can(BrowseTheWeb.with(browser));
}

@Test
public void whenGoogleForEachThenShouldSeeForEach() {
givenThat(kitty).wasAbleTo(StartWith.googleSearchPage());

when(kitty).attemptsTo(SearchForKeyword.of("foreach"));

then(kitty).should(seeThat(GoogleSearchResults.displayed(),
hasItem(containsString("ForEach (ForEach)"))));
}
}

После запуска этого теста мы увидим скриншоты каждого шага, выполненного Китти, в отчете о тестировании:

./9fddecf307a505da46f2eb8d8336986c.png

6. Резюме

В этой статье мы рассказали, как использовать шаблон сценария с Serenity BDD. Кроме того, с помощью PageObject нам не нужно напрямую взаимодействовать с WebDrivers, что упрощает чтение, обслуживание и расширение наших тестов.

Дополнительные сведения о PageObject и шаблоне сценария в Serenity BDD см. в соответствующем разделе документации Serenity.

Как всегда, полный код примера можно найти на Github .