Índice
- Introdução
1.1 Agradecimentos
1.2 Pré-Requisitos
1.3 Setup Inicial
- Python
2.1 Instalando Python
2.2 Instalando Pipenv
- Criando o Projeto
3.1 Instalando ambiente python localmente no projeto
- Pytest
4.1 Executando Pytest
4.2 Convenções do Pytest
- Selenium WebDriver
5.1 Convenções do Selenium WebDriver
5.2 Instalando ChromeDriver no Windows
5.3 Instalando selenium package para python
- Page Objects
7.1 Convenções do Page Objects
7.2 Antes do Page Objects
7.3 Declarando osPage Objects
- Encontrando locators para os elementos
8.1 Identificando os elementos
8.2 Estrutura de um locator
8.3 Declarando os locators em python
- Chamadas WebDriver
9.1 Asterisco como argumento no Find Element
9.2 Implementação
- Repositorio
10.1 README.md
10.2 .gitignore
- Conclusão
11.1 O que fica pra depois
1. Introdução
Criei esse tutorial para aprender mais sobre automação de testes em python para aplicações web.
Nesse tutorial documentei como fazer o setup inicial do ambiente python e pytest no windows, escrever os testes usando a sintaxe gherkin, implementar o design pattern page object e realizar as chamadas do selenium webdriver no google chrome.
1.1 Agradecimentos
Esse tutorial foi feito com base no curso “Selenium WebDriver with Python”, ministrado por Andrew Knight.
Ele também possui um blog, clique no link para acompanhar as novidades, https://automationpanda.com/.
1.2 Pré-Requisitos
Esse tutorial tem foco em automação de testes, por isso alguns conhecimentos prévios em Python e Pytest serão necessários.
Caso queira conhecer um pouco mais sobre Python e Pytest clique nos links para ver os cursos gratuitos.
1.3 Setup inicial
O editor de texto utilizado neste projeto é o VS Code (VisualStudio), para baixá-lo clique no link abaixo.
A seguir estão listadas as ferramentas que serão utilizadas no projeto, durante o tutorial será explicado como instalá-las corretamente.
2. Python
2.1 Instalando Python
Verifique se seu computador já possui o Python instalado.
Abra o “Prompt de Comando” e execute o comando a seguir:
python --version
Se exibir a mensagem que o python não foi encontrado, então vá para https://www.python.org/downloads/ e baixe a versão disponível:
Obs.: É necessário selecionar a opção “Add Python 3.10 to PATH” para que a instalação configure as variáveis de ambiente:
Após instalar o Python feche e abra o “Prompt de Comando” e execute o comando abaixo:
python --version
Python instalado com sucesso.
2.2 Instalando Pipenv
O pipenv foi instalado junto ao Python, verifique isso ao abrir o “Prompt de Comando” e executar o seguinte comando:
pipenv --version
Pipenv instalado com sucesso.
Obs.: Caso o comando “pipenv --version
” exiba a mensagem que o comando pipenv não é reconhecido, instale-o executando o comando abaixo:
pip install pipenv
Se mesmo assim o problema persistir siga as instruções abaixo.
- Desinstale a versão atual do virtualenv: “
pip uninstall virtualenv
” - Desinstale a versão atual do pipenv: “
pip uninstall pipenv
” - Quando perguntado sobre prosseguir (y/n) confirme com y
- Finalizado, instale pipenv e suas dependências: “
pip install pipenv
”
Após isso, o comando “pipenv --version
” deverá exibir a versão do pipenv que foi instalada, não se esqueça de fechar e abrir o “Prompt de Comando”.
3. Criando o Projeto
Cria uma pasta com o nome do seu projeto.
3.1 Instalando ambiente python localmente no projeto
Importante fazer isso localmente para também conseguir gerenciar as dependências de forma local.
Abra o “Prompt de Comando” dentro da pasta criada.
E rode o seguinte comando:
pipenv install
Após rodar o comando dentro da pasta, abra o projeto pelo VSCode e abra o arquivo “PipFile” que foi criado pelo comando.
Dentro do arquivo, logo abaixo do parâmetro [packages] declare as ferramentas que serão usadas nesse projeto (pytest e selenium):
[packages]
pytest = "*"
selenium = "*"
OB.: É importante que o parâmetro [python_version] dentro do arquivo pipfile esteja na mesma versão que a do python instalado na sua máquina.
[requires]
python_version = "3.10"
Após salvar as alterações no projeto, execute o comando “pipenv install
” no “Prompt de Comando” novamente para instalar as novas dependências adicionadas.
4. Pytest
4.1 Executando Pytest
Para executar os testes, abra o “Prompt de Comando” dentro da pasta do projeto e execute o seguinte comando:
pipenv run python -m pytest
4.2 Convenções do Pytest
Os testes devem ficar na pasta “tests” na raíz do projeto, seguindo a regra “<project-root>/tests”.
Crie a pasta “tests” como no exemplo abaixo:
Os nomes dos arquivos (módulos) dos testes devem ter os nomes escritos de forma que começam com “test_” ou terminam com “_test”.
Exemplo: “test_*.py” ou “*_test.py”
Funções devem começar com o prefixo “test” em seus nomes.
Ex: “def test_basic_duckduckgo_search():”
def test_basic_duckduckgo_search(browser):
5. Selenium WebDriver
O selenium webdriver é um pacote de código aberto gratuito para automatizar interações com um navegador.
É W3C standard, para saber mais https://selenium-python.readthedocs.io/.
É apenas um pacote de programação que precisa de um servidor proxy para se conectar com um navegador, por exemplo, precisa do chromedriver para se comunicar com o google chrome.
5.1 Convenções do Selenium Webdriver
Todo teste deveria iniciar e finalizar sua própria instância do webdriver, seguindo a premissa: um teste, um webdriver, um browser.
Todo caso de teste deve ser independente um do outro, não compartilhando recursos (compartilhar recursos pode atrapalhar a paralelização dos testes).
Sempre use o método “quit” do webdriver, caso seja usado o “close” no lugar do “quit” processos podem continuar rodando consumindo recursos da máquina.
5.2 Instalando Chromedriver no Windows
Neste tutorial será utilizado o chromedriver (para Chrome), que será instalado no system path do windows, e não dentro da pasta do projeto como de costume em vários exemplos.
Baixe a versão mais nova estável do chromedriver do link abaixo: https://chromedriver.chromium.org/home
Importante ser a mesma versão do chrome instalado na sua máquina:
Extraia o executável na pasta (C:\webdrivers\).
Vá até o caminho da pasta (C:\webdrivers\) pelo terminal e digite o seguinte comando para verificar se a versão instalada foi a correta:
chromedriver.exe --version
Abra o programa variáveis de ambiente.
Em variáveis de sistema selecione PATH e clique em editar.
Clique em NOVO e adicione o caminho da pasta onde o chromedriver foi extraído.
Clique em OK, abra um novo “Prompt de Comando” e digite o comando:
chromedriver --version
5.3 Instalando selenium package para python
Pelo “Prompt de Comando” vá até a pasta do projeto e execute o seguinte comando:
pipenv install selenium
6. Fixture e injeção de dependência no python
Fixtures são funções que são chamadas antes de cada teste, nela serão incluídas etapas de setup e clean up em um corpo.
Crie um arquivo com o nome “conftest.py” na pasta dos testes “<project-root>/tests”.
O código a seguir exemplifica a estrutura de um arquivo com uma função fixture, as etapas de setup e clean up que ficam encapsuladas nele.
"""
This module contains shared fixture.
"""
import pytest
import selenium.webdriver
@pytest.fixture
def browser():
# Initialize the Chromedriver instance
b = selenium.webdriver.Chrome()
# Make its calls wait up 10 seconds for elements to appear
b.implicitly_wait(10)
# Return the WebDriver instance for the setup
yield b
# Quit the WebDriver instance for the instance
b.quit
A função fixture é invocada quando uma função de testes recebe um parâmetro com o mesmo nome da função fixture.
Assim, as variáveis da fixture ficam visíveis dentro do teste.
É uma maneira elegante de injeção de dependência.
def test_basic_duckduckgo_search(browser):
7. Page Objects
É um objeto representando uma página web ou componente.
Possui locators para encontrar page elements e métodos de interação que interagem com a página que está sendo testada.
7.1 Convenções do Page Objects
A pasta “pages” pode ficar fora da pasta “tests”.
Ao ficar do lado de fora da pasta “tests”, o que estiver em “pages” pode ser tratado como um python package e ser importado dentro dos testes.
Dentro da raiz do projeto crie a pasta “pages”, dentro de “pages” crie um arquivo chamado “__init__.py”.
Obs.: Todas as classes tem uma função chamada __init__(), que é sempre executada quando a classe está sendo inicializada.
Para saber mais sobre como __init__ funciona vá para:
https://www.w3schools.com/python/gloss_python_class_init.asp
Será um arquivo vazio 🙂
7.2 Antes do Page Objects
Antes de declarar os page objects é importante lembrar que o foco é sempre testar a regra de negócio, sempre comece escrevendo os testes usando a sintaxe gherkin ao invés de apenas sair programando sua automação.
Segue abaixo o teste que iremos automatizar em nosso projeto, escreva-o no arquivo “test_search”.
"""
These tests cover DuckDuckGo searches.
"""
def test_basic_duckduckgo_search():
# Given the DuckDuckGo home page is displayed
# When the user searches for "panda"
# Then the search result title contains "panda"
# And the search result query is "panda"
# And the search result links pertain to "panda"
raise Exception("Incomplete Test")
7.3 Declarando os Page Objects
Por enquanto iremos codificar apenas a estrutura base das duas páginas mapeadas do site que será testado (DuckDuckGo).
Essas páginas são search (realizar as buscas no site) e result (a listagem dos sites encontrados).
Após codificar as estruturas e fixar o conceito do page objets vamos identificar os locators e implementar as chamadas webdrivers para manipular os elementos web.
Crie os arquivos “search.py” e “result.py” dentro da pasta ”pages”.
search.py
class DuckDuckGoSearchPage:
def __init__(self, browser):
self.browser = browser
def load(self):
# TODO
pass
def search(self):
# TODO
Pass
result.py
class DuckDuckGoResultPage:
def __init__(self, browser):
self.browser = browser
def title(self):
# TODO
return ""
def search_input_value(self):
# TODO
return ""
def result_link_titles(self):
# TODO
return []
8. Encontrando locators para os elementos
Elements é basicamente tudo o que está na página web, pode ser um botão, label ou até mesmo um input text.
Locators são queries que encontram os elementos na página e retornam os elementos que combinam com a query.
Exemplo:
OB.: O “page title” não é um web element, mas sim um atributo da página.
8.1 Identificando os elementos
Nesse exemplo, para identificar os elementos na página web e conseguir declará-los no seu teste utilizaremos o google chrome.
- Vá até o www.duckduckgo.com.
- Clique com o botão direito do mouse no elemento que você quer identificar e selecione a opção inspect.
O navegador irá abrir o inspect, exibindo os atributos do elemento, possibilitando selecionar o id do mesmo.
8.2 Estrutura de um locator
A estrutura de um locator se divide entre duas partes, o “locator type” e a “query string”, exemplo, (By.ID, ‘id_do_locator’).
Existem vários “locator type” que podem ser utilizados para identificar um web element, junto desses “locator type” deve ser utilizado a “query string” que possui o valor encontrado no “element” que queremos.
Segue alguns tipos de “locator type”.
- By.ID
- By.NAME
- By.CLASS_NAME
- By.CSS_SELECTOR
- By.XPATH
- By.LINK_TEXT
- By.PARTIAL_LINK_TEXT
- By.TAG_NAME
8.3 Declarando os locators em python
Declare os locators dentro das classes page objects, representando os elementos delas.
class DuckDuckGoResultPage:
RESULT_LINKS = (By.CSS_SELECTOR, 'a.result__a')
SEARCH_INPUT = (By.ID, 'search_form_input')
9. Chamadas WebDriver
Para WebDriver: | Para Elementos: |
Lista de chamadas que podemos realizar diretamente da instância do webdriver (nomeada “browser” no código fonte). | Lista de chamadas que podemos realizar nos objetos retornados pelos métodos “find_element”. |
• current_url • find_element • find_elements • find_element_by_* • get • maximize_window • quit • refresh • save_screenshot • title | • clear • click • find_element* • get_attribute • get_property • is_displayed • location • send_keys • size • text |
Obs.: O método “text” de um elemento web do tipo input de texto retorna um texto vazio, para pegar o texto que foi digitado é necessário utilizar o método “get_attribute(‘value’)”. |
9.1 Asterisco como argumento no Find Element
O método find_element recebe dois argumentos, o “locator type” e a “query string”, mas a variável “search input” é uma tupla (com duas colunas).
O asterisco é usado no python para expandir as tuplas em “positional arguments” que podem ser passados nos métodos.
SEARCH_INPUT = (By.ID, 'search_form_input_homepage')
def search(self, phrase):
self.browser.find_element(*self.SEARCH_INPUT)
pass
9.2 Implementação
tests/test_search.py
"""
These tests cover DuckDuckGo searches.
"""
from pages.search import DuckDuckGoSearchPage
from pages.result import DuckDuckGoResultPage
def test_basic_duckduckgo_search(browser):
search_page = DuckDuckGoSearchPage(browser)
result_page = DuckDuckGoResultPage(browser)
PHRASE = "panda"
# Given the DuckDuckGo home page is displayed
search_page.load()
# When the user searches for "panda"
search_page.search(PHRASE)
# Then the search result title contains "panda"
assert PHRASE in result_page.title()
# And the search result query is "panda"
assert PHRASE == result_page.search_input_value()
# And the search result links pertain to "panda"
# for title in result_page.result_link_titles():
# assert PHRASE.lower() in title.lower()
# raise Exception("Incomplete Test")
Obs.: A última validação do teste foi comentada porque a página “result” retornou alguns sites que não possuem a string “panda” em seu título.
pages/search.py
"""
This module contains DuckDuckGoSearchPage,
the page object for the DuckDuckGo search page.
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
class DuckDuckGoSearchPage:
# URL
URL = 'https://www.duckduckgo.com'
# Locators
SEARCH_INPUT = (By.ID, 'search_form_input_homepage')
# Initializer
def __init__(self, browser):
self.browser = browser
# Interaction Methods
def load(self):
self.browser.get(self.URL)
def search(self, phrase):
search_input = self.browser.find_element(*self.SEARCH_INPUT)
search_input.send_keys(phrase + Keys.RETURN)
pages/result.py
"""
This module contains DuckDuckGoResultPage,
the page object for the DuckDuckGo search result page.
"""
from selenium.webdriver.common.by import By
class DuckDuckGoResultPage:
# Locators
SEARCH_INPUT = (By.ID, 'search_form_input')
RESULT_LINKS = (By.CSS_SELECTOR, 'a.result__a')
# Initializer
def __init__(self, browser):
self.browser = browser
# Interaction Methods
def title(self):
return self.browser.title
def search_input_value(self):
search_input = self.browser.find_element(*self.SEARCH_INPUT)
value = search_input.get_attribute('value')
return value
def result_link_titles(self):
links = self.browser.find_elements(*self.RESULT_LINKS)
titles = [link.text for link in links]
return titles
10. Repositório
Link do repositório do projeto:
https://github.com/LuizGustavoR/intro-selenium-py/tree/tutorial/webdriver-with-python
10.1 README.md
É um arquivo escrito no formato markdown, basicamente este formato define uma série de tags para formatar um documento de texto.
Na raiz do seu projeto crie um arquivo com o nome “README.md”.
Segue o link do “README.md” no repositório:
https://github.com/LuizGustavoR/intro-selenium-py/blob/tutorial/webdriver-with-python/README-pt-BR.md
10.2 .gitignore
Um arquivo “.gitignore” específica arquivos intencionalmente não rastreados que o Git deveria ignorar.
Arquivos já rastreados pelo Git não são afetados.
Crie um arquivo com o nome “.gitignore” na raiz do seu projeto.
Segue o link do “.gitignore” no repositório:
https://github.com/LuizGustavoR/intro-selenium-py/blob/tutorial/webdriver-with-python/.gitignore
11. Conclusão
Realizados todos os passos, agora é só rodar os testes executando o seguinte comando no prompt.
pipenv run python -m pytest
Sucesso.
Parabéns, o teste foi executado como esperado.
11.1 O que fica pra depois
Esse é um simples tutorial que ensina como instalar o python e criar um projeto de automação do zero usando selenium.
Então rodar em múltiplos navegadores, rodar os testes de forma paralela, integrar esse projeto em um CI ou até mesmo dockerizar fica pra outro tutorial.
Fim.
12. Bibliografia
- https://testautomationu.applitools.com/selenium-webdriver-python-tutorial/
- https://www.python.org/
- https://pypi.org/project/pipenv/
- https://stackoverflow.com/questions/46041719/windows-reports-error-when-trying-to-install-package-using-pipenv
- https://selenium-python.readthedocs.io/
- https://chromedriver.chromium.org/home
- https://www.w3schools.com/python/gloss_python_class_init.asp
- https://www.treinaweb.com.br/blog/criando-um-readme-para-seu-perfil-do-github
- https://qastack.com.br/programming/27850222/what-is-gitignore-exactly
- http://git-scm.com/docs/gitignore