Serenity BDD e Roteiro
1. Visão geral
Neste artigo, daremos uma olhada rápida no padrão de roteiro no Serenity BDD. Sugerimos que você leia obasics of Serenity BDD primeiro antes de ler este. Além disso, o artigo sobreSerenity BDD integration with Spring também pode ser interessante.
O roteiro, introduzido no Serenity BDD, visa incentivar bons hábitos de teste e conjuntos de testes bem projetados, permitindo que as equipes escrevam testes mais robustos e confiáveis. É baseado no Selenium WebDriver e no modelo de Objetos de Página. Se você leu nossointroduction to Selenium, encontrará esses conceitos bastante familiares.
2. Dependência do Maven
Primeiro, vamos adicionar as seguintes dependências ao arquivopom.xml:
net.serenity-bdd
serenity-junit
1.4.0
net.serenity-bdd
serenity-screenplay
1.4.0
net.serenity-bdd
serenity-screenplay-webdriver
1.4.0
As versões mais recentes deserenity-screenplayeserenity-screenplay-webdriver podem ser obtidas no Repositório Central Maven.
Também precisamos de drivers da web para fazer o roteiro -ChromeDriver ouMozilla-GeckoDriver bastará. Neste artigo, usaremos o ChromeDriver.
A seguinte configuração de plug-in é necessária para habilitar o WebDriver, em que o valor dewebdriver.chrome.driver deve ser o caminho relativo do arquivo binário ChromeDriver em nosso projeto maven:
maven-failsafe-plugin
2.20
chromedriver
3. Suporte WebDriver
Podemos fazer o Serenity gerenciar a instância do WebDriver marcando a anotação@Managed em uma variável do WebDriver. O Serenity abrirá um driver apropriado no início de cada teste e o desligará quando o teste for concluído.
No exemplo a seguir, iniciamos um ChromeDriver e abrimos o Google para pesquisar por 'exemplo'. Esperamos que o nome de Eugen esteja presente nos resultados da pesquisa:
@RunWith(SerenityRunner.class)
public class GoogleSearchLiveTest {
@Managed(driver = "chrome")
private WebDriver browser;
@Test
public void whenGoogleexampleThenShouldSeeEugen() {
browser.get("https://www.google.com/ncr");
browser
.findElement(By.name("q"))
.sendKeys("example", Keys.ENTER);
new WebDriverWait(browser, 5)https://www.example.com/serenity-screenplay
.until(visibilityOfElementLocated(By.cssSelector("._ksh")));
assertThat(browser
.findElement(By.cssSelector("._ksh"))
.getText(), containsString("Eugen (example)"));
}
}
Se não especificarmos nenhum parâmetro para@Managed, o Serenity BDD usará o Firefox neste caso. A lista completa de drivers suportados pela anotação@Managed:firefox, chrome, iexplorer, htmlunit, phantomjs.
Se precisarmos testar no IExplorer ou Edge, podemos baixar os drivers da web dehere(for IE)ehere(for Edge) respectivamente. Safari WebDriver está disponível apenas em MacOS em/usr/bin/safaridriver.
4. Objetos de página
Objetos de página de serenidade representam um objeto de página do WebDriver. OPageObject oculta os detalhes do WebDriver para reutilização.
4.1. Exemplo de refatoração usandoPageObject
Vamos refinar nosso teste anterior usandoPageObject primeiro extraindo as ações de localização, pesquisa e verificação de resultados de elementos:
@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 representa um elemento HTML. Podemos interagir com páginas da web por meio de APIs da interface. No exemplo acima, usamos duas maneiras de localizar elementos da web na página: pelo nome do elemento e pelas classes CSS do elemento.
Existem mais abordagens para aplicar ao localizar elementos da Web, como localizar pelo nome da tag, localizar pelo texto do link etc. Consulte nossoguide to Selenium para mais detalhes.
Também podemos substituirWebElement porWebElementFacade, que fornece APIs mais fluentes para lidar com elementos da web.
ComoSerenity will automatically instantiate any PageObject fields in the JUnit test, o teste anterior pode ser reescrito em um muito mais limpo:
@RunWith(SerenityRunner.class)
public class GoogleSearchPageObjectLiveTest {
@Managed(driver = "chrome")
private WebDriver browser;
GoogleSearchPageObject googleSearch;
@Test
public void whenGoogleexampleThenShouldSeeEugen() {
googleSearch.open();
googleSearch.searchFor("example");
googleSearch.resultMatches("Eugen (example)");
}
}
Agora podemos pesquisar usando outras palavras-chave e corresponder ao resultado da pesquisa relacionado sem fazer nenhuma alteração emGoogleSearchPageObject.
4.2. Suporte Assíncrono
Atualmente, muitas páginas da web são exibidas ou renderizadas dinamicamente. Para lidar com esses casos,PageObject também oferece suporte a muitos recursos avançados que nos permitem inspecionar o status dos elementos. We can check if the elements are visible, or wait until they are visible before proceeding.
Vamos aprimorar o métodoresultMatches garantindo que o elemento que queremos ver esteja visível:
public void resultMatches(String expected) {
waitFor(result).waitUntilVisible();
assertThat(result.getText(), containsString(expected));
}
Se não esperamos esperar muito tempo, podemos especificar explicitamente o tempo limite nas ações de espera:
public void resultMatches(String expected) {
withTimeoutOf(5, SECONDS)
.waitFor(result)
.waitUntilVisible();
assertThat(result.getText(), containsString(expected));
}
5. Padrão de Roteiro
O Padrão de Roteiro aplica os princípios de design do SOLID aos testes de aceitação automatizados. Uma compreensão geral do Padrão de Roteiro pode ser explicada no contexto degiven_when_then como:
-
given - umActor que é capaz de realizar algunsTask
-
when - oActor realiza oTask
-
then –Actor deve ver o efeito e verificar os resultados
Agora, vamos encaixar nosso cenário de teste anterior no Padrão de Roteiro: dado um usuário Kitty que pode usar o Google, quando ela pesquisa "exemplo" no Google, então Kitty deve ver o nome de Eugen nos resultados.
Primeiro, defina as tarefas que Kitty pode executar.
-
Kitty pode usar o Google:
public class StartWith implements Task { public static StartWith googleSearchPage() { return instrumented(StartWith.class); } GoogleSearchPage googleSearchPage; @Step("{0} starts a google search") public
void performAs(T t) { t.attemptsTo(Open .browserOn() .the(googleSearchPage)); } } -
Kitty pode fazer uma pesquisa no Google:
public class SearchForKeyword implements Task { @Step("{0} searches for '#keyword'") public
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); } } -
Kitty pode ver os resultados de pesquisa do Google:
public class GoogleSearchResults implements Question
- > {
public static Question
- > displayed() {
return new GoogleSearchResults();
}
public List
answeredBy(Actor actor) { return Text .of(GoogleSearchPage.SEARCH_RESULT_TITLES) .viewedBy(actor) .asList(); } }
Além disso, já definimos a pesquisa do GooglePageObject:
@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");
}
Agora, nossa principal classe de teste seria semelhante a:
@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 whenGoogleexampleThenShouldSeeEugen() {
givenThat(kitty).wasAbleTo(StartWith.googleSearchPage());
when(kitty).attemptsTo(SearchForKeyword.of("example"));
then(kitty).should(seeThat(GoogleSearchResults.displayed(),
hasItem(containsString("Eugen (example)"))));
}
}
Depois de executar este teste, veremos capturas de tela de cada etapa que Kitty executou no relatório de teste:
6. Sumário
Neste artigo, apresentamos como usar o Screenplay Pattern com o Serenity BDD. Além disso, com a ajuda dePageObject, não precisamos interagir com WebDrivers diretamente, tornando nossos testes mais fáceis de ler, manter e estender.
Para obter mais detalhes sobrePageObjecte Padrão de Roteiro no Serenity BDD, verifique a seção relacionada da documentação do Serenity.
Como sempre, o código de exemplo completo pode ser encontrado emon the Github.