Depois de concluirmos esta aula, nós:
- Saberemos como podemos abrir uma página web com o Selenium WebDriver (e Playwright).
- Entenderemos como podemos clicar em um elemento HTML e aguardar a navegação com
WebDriverWait.
- Saberemos como preencher campos de texto,
<select>, checkboxes, radio buttons e submeter um formulário HTML.
1. Abrindo uma Página Web
Podemos abrir uma página web invocando o método get() da interface WebDriver (ou page.navigate() no Playwright). Esse método abre a página solicitada enviando uma requisição HTTP GET e recebe o endereço URL da página web como parâmetro.
Depois de escrevermos um teste End-to-End que abre uma nova página web e verifica o título da aba (browser.title), o código-fonte da nossa classe de teste fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.testwithspring.intermediate.web
import com.testwithspring.intermediate.SeleniumTest
import com.testwithspring.intermediate.SeleniumWebDriver
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class RenderClickSourcePageTest {
@SeleniumWebDriver
lateinit var browser: WebDriver
@Test
fun `deve abrir a pagina de origem do clique`() {
browser.get("http://localhost:8080/click-source")
assertThat(browser.title).isEqualTo("Click Source Page")
}
}
|
Devemos entender que o método get() bloqueia a execução até que a página solicitada seja carregada. Ele também seguirá todos os redirecionamentos emitidos pelo servidor HTTP (como 302 Found) ou pelo próprio documento HTML retornado (meta-redirects imediatos).
Entretanto, existem duas situações importantes nas quais não podemos confiar que o método get() carregou o DOM inteiro antes de retornar:
- A página carregada utiliza JavaScript assíncrono que faz alterações no DOM (como buscar dados via AJAX/Fetch após o carregamento inicial). Se esse for o caso, nosso teste deve aguardar até que o DOM tenha sido construído.
- A página retornada possui um meta-redirect com temporizador que aguarda alguns segundos antes de redirecionar o navegador para outra página. Se esse for o caso, nosso teste deve aguardar até que esse tempo expire e o redirecionamento ocorra; caso contrário, nosso teste poderá inspecionar a página errada!
A próxima seção desta aula descreve exatamente como podemos escrever testes End-to-End que clicam em elementos e aguardam até que uma condição específica seja verdadeira.
Agora escreveremos um teste End-to-End que demonstra como podemos clicar em um elemento encontrado no documento HTML carregado. Nosso teste clicará em um link presente na página HTML de origem (/click-source), cujo código-fonte é o seguinte:
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html>
<html>
<head>
<title>Click Source Page</title>
</head>
<body>
<a id="click-target-link" href="/click-target">Click this link</a>
</body>
</html>
|
Quando clicamos nesse link #click-target-link, nossa aplicação abre um segundo documento HTML (/click-target), cujo código-fonte é o seguinte:
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html>
<html>
<head>
<title>Click Target Page</title>
</head>
<body>
<p>This is the click target page</p>
</body>
</html>
|
Podemos escrever nosso teste End-to-End seguindo estes quatro passos:
Passo 1: Criar a Classe de Teste e Abrir a Página Inicial no @BeforeEach
Primeiro, criamos uma classe de teste End-to-End chamada ClickTargetLinkTest e garantimos que a primeira página (/click-source) seja carregada antes da execução do nosso método de teste:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.testwithspring.intermediate.web
import com.testwithspring.intermediate.SeleniumTest
import com.testwithspring.intermediate.SeleniumWebDriver
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Tag
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class ClickTargetLinkTest {
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openClickSourcePage() {
browser.get("http://localhost:8080/click-source")
}
}
|
Passo 2: Localizar o Link pelo ID e Invocar click()
Segundo, escrevemos um método de teste que localiza o elemento de link utilizando seu id ("click-target-link") e clica no link encontrado invocando targetLink.click():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class ClickTargetLinkTest {
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openClickSourcePage() {
browser.get("http://localhost:8080/click-source")
}
@Test
fun `deve abrir a pagina de destino ao clicar no link`() {
val targetLink = browser.findElement(By.id("click-target-link"))
targetLink.click()
}
}
|
Terceiro, como o método click() no Selenium WebDriver não aguarda necessariamente até que a próxima página termine de carregar, precisamos garantir que nosso teste espere pelo tempo que for necessário (até um limite máximo). Podemos fazer isso em dois subpassos:
- Criar um novo objeto
WebDriverWait, passando a instância do browser e o timeout máximo de espera (por exemplo, Duration.ofSeconds(3) no Selenium 4 / Spring Boot 4, equivalente ao construtor WebDriverWait(browser, 3) do Selenium 3).
- Invocar o método
until() da classe WebDriverWait. Ao invocarmos esse método, especificamos a condição que deve retornar true para que nosso teste End-to-End pare de esperar. A classe ExpectedConditions fornece métodos fábrica estáticos ideais para a maioria dos casos de uso — utilizaremos ExpectedConditions.urlToBe("http://localhost:8080/click-target") para aguardar até que a URL da página atual seja a URL de destino.
Nota sobre o Playwright: No Playwright, o método page.locator("#click-target-link").click() e as asserções assertThat(page).hasURL(...) já possuem auto-waiting nativo embutido, aplicando exatamente essa mesma lógica de polling sob o capô!
Depois de adicionarmos a espera explícita ao nosso teste, o código-fonte fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class ClickTargetLinkTest {
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openClickSourcePage() {
browser.get("http://localhost:8080/click-source")
}
@Test
fun `deve abrir a pagina de destino ao clicar no link`() {
val targetLink = browser.findElement(By.id("click-target-link"))
targetLink.click()
val wait = WebDriverWait(browser, Duration.ofSeconds(3))
wait.until(
ExpectedConditions.urlToBe("http://localhost:8080/click-target")
)
}
}
|
Passo 4: Verificar o Título da Página Carregada
Quarto, verificamos se a página carregada possui o título esperado ("Click Target Page"). O código final da nossa classe ClickTargetLinkTest fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package com.testwithspring.intermediate.web
import com.testwithspring.intermediate.SeleniumTest
import com.testwithspring.intermediate.SeleniumWebDriver
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class ClickTargetLinkTest {
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openClickSourcePage() {
browser.get("http://localhost:8080/click-source")
}
@Test
fun `deve abrir a pagina de destino ao clicar no link`() {
val targetLink = browser.findElement(By.id("click-target-link"))
targetLink.click()
val wait = WebDriverWait(browser, Duration.ofSeconds(3))
wait.until(
ExpectedConditions.urlToBe("http://localhost:8080/click-target")
)
assertThat(browser.title).isEqualTo("Click Target Page")
}
}
|
Vamos descobrir a seguir como podemos preencher e submeter um formulário HTML completo.
Agora escreveremos um teste End-to-End que preenche os campos de um formulário, submete-o e verifica se as informações corretas são exibidas na página de resultado.
O template HTML/JSP que renderiza o formulário submetido (/form) possui a seguinte estrutura:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<!DOCTYPE html>
<html>
<head>
<title>Example Form Page</title>
</head>
<body>
<h1 id="page-header">Example Form Page</h1>
<form id="form" action="/form" method="POST" role="form">
<div>
<label for="message">Message</label>
<input id="message" name="message" type="text"/>
</div>
<div>
<label for="number">Number</label>
<select id="number" name="number">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<div>
<label for="checkbox">Checkbox</label>
<input id="checkbox" name="checkbox" type="checkbox" value="true"/>
</div>
<div>
<label for="radio-button">RadioButton</label>
<input id="radio-button" name="radioButton" type="radio" value="true"/>
</div>
<button type="submit">Submit</button>
</form>
</body>
</html>
|
Quando submetemos esse formulário, nossa aplicação renderiza uma página de resultado que exibe os valores preenchidos nos campos do formulário:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<!DOCTYPE html>
<html>
<head>
<title>Form Result Page</title>
</head>
<body>
<h1 id="page-header">Form Result Page</h1>
<div id="form-content">
<div id="form-message">
<div id="form-message-label">Message: </div>
<div id="form-message-value">${form.message}</div>
</div>
<div id="form-number">
<div id="form-number-label">Number: </div>
<div id="form-number-value">${form.number}</div>
</div>
<div id="form-checkbox">
<div id="form-checkbox-label">Checkbox: </div>
<div id="form-checkbox-value">${form.checkbox}</div>
</div>
<div id="form-radio-button">
<div id="form-radio-button-label">Radio Button: </div>
<div id="form-radio-button-value">${form.radioButton}</div>
</div>
</div>
</body>
</html>
|
Podemos escrever nosso teste End-to-End para esse formulário seguindo estes quatro passos:
Primeiro, criamos a classe de teste SubmitExampleFormTest e garantimos que a página do formulário (http://localhost:8080/form) seja carregada no método @BeforeEach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.testwithspring.intermediate.web
import com.testwithspring.intermediate.SeleniumTest
import com.testwithspring.intermediate.SeleniumWebDriver
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Tag
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class SubmitExampleFormTest {
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openFormPage() {
browser.get("http://localhost:8080/form")
}
}
|
Segundo, adicionamos duas constantes à nossa classe de teste (MESSAGE e NUMBER), que especificam os valores que serão digitados e selecionados nos campos do formulário:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class SubmitExampleFormTest {
companion object {
private const val MESSAGE = "Hello world"
private const val NUMBER = "2"
}
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openFormPage() {
browser.get("http://localhost:8080/form")
}
}
|
Terceiro, implementamos um método privado chamado fillAndSubmitForm() que preenche todos os tipos de campos do formulário e o submete:
- Campo de texto (
<input type="text">): Localizamos o elemento #message e invocamos sendKeys(MESSAGE) para digitar o texto.
- Menu dropdown (
<select>): Envolvemos o WebElement #number em um objeto Select (org.openqa.selenium.support.ui.Select) e invocamos selectByValue(NUMBER).
- Checkbox (
<input type="checkbox">): Localizamos o elemento #checkbox e invocamos click() para marcá-lo.
- Radio Button (
<input type="radio">): Localizamos o elemento #radio-button e invocamos click() para selecioná-lo.
- Submissão do
<form>: Localizamos o elemento #form e invocamos submit().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private fun fillAndSubmitForm() {
val messageInput = browser.findElement(By.id("message"))
messageInput.sendKeys(MESSAGE)
val numberSelect = Select(browser.findElement(By.id("number")))
numberSelect.selectByValue(NUMBER)
val checkbox = browser.findElement(By.id("checkbox"))
checkbox.click()
val radioButton = browser.findElement(By.id("radio-button"))
radioButton.click()
browser.findElement(By.id("form")).submit()
}
|
Importante: Devemos entender que o método submit() de um formulário tradicional (Server-Side Rendered) bloqueia a execução até que a nova página seja carregada. Isso significa que não precisamos adicionar lógica extra de WebDriverWait após chamar submit() em formulários HTML síncronos!
Passo 4: Escrever o Método de Teste e Verificar a Página de Resultado
Quarto, escrevemos o método de teste que invoca fillAndSubmitForm() e verifica se os quatro valores exibidos na página de resultado correspondem aos dados submetidos. O código completo da classe SubmitExampleFormTest fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
package com.testwithspring.intermediate.web
import com.testwithspring.intermediate.SeleniumTest
import com.testwithspring.intermediate.SeleniumWebDriver
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.support.ui.Select
@SeleniumTest(driver = ChromeDriver::class)
@Tag("endToEndTest")
class SubmitExampleFormTest {
companion object {
private const val MESSAGE = "Hello world"
private const val NUMBER = "2"
}
@SeleniumWebDriver
lateinit var browser: WebDriver
@BeforeEach
fun openFormPage() {
browser.get("http://localhost:8080/form")
}
@Test
fun `deve renderizar a pagina de resultado do formulario com os valores corretos`() {
fillAndSubmitForm()
val message = browser.findElement(By.id("form-message-value"))
assertThat(message.text).isEqualTo(MESSAGE)
val number = browser.findElement(By.id("form-number-value"))
assertThat(number.text).isEqualTo(NUMBER)
val checkbox = browser.findElement(By.id("form-checkbox-value"))
assertThat(checkbox.text).isEqualTo("true")
val radioButton = browser.findElement(By.id("form-radio-button-value"))
assertThat(radioButton.text).isEqualTo("true")
}
private fun fillAndSubmitForm() {
val messageInput = browser.findElement(By.id("message"))
messageInput.sendKeys(MESSAGE)
val numberSelect = Select(browser.findElement(By.id("number")))
numberSelect.selectByValue(NUMBER)
val checkbox = browser.findElement(By.id("checkbox"))
checkbox.click()
val radioButton = browser.findElement(By.id("radio-button"))
radioButton.click()
browser.findElement(By.id("form")).submit()
}
}
|
Resumo
Esta aula nos ensinou cinco coisas fundamentais:
- Podemos abrir uma página web utilizando o método
get() da interface WebDriver.
- Podemos clicar em qualquer elemento encontrado em um documento HTML utilizando o método
click() da interface WebElement.
- Podemos submeter um formulário HTML utilizando o método
submit() da interface WebElement.
- Se clicarmos em um link (
click()), precisamos adicionar uma lógica de espera explícita (WebDriverWait + ExpectedConditions) ao nosso teste End-to-End, porque o método click() não aguarda automaticamente o carregamento completo da nova página no Selenium.
- Se abrirmos uma página web com
get() ou submetermos um formulário HTML tradicional com submit(), o WebDriver aguarda até que a nova página seja carregada antes de prosseguir.