Selenium Webdriver com Python


Índice

  1. Introdução
    1.1 Agradecimentos
    1.2 Pré-Requisitos
    1.3 Setup Inicial
  1. Python
    2.1 Instalando Python
    2.2 Instalando Pipenv
  1. Criando o Projeto
    3.1 Instalando ambiente python localmente no projeto
  1. Pytest
    4.1 Executando Pytest
    4.2 Convenções do Pytest
  1. Selenium WebDriver
    5.1 Convenções do Selenium WebDriver
    5.2 Instalando ChromeDriver no Windows
    5.3 Instalando selenium package para python
  1. Fixture e injeção de dependência no python
  1. Page Objects
    7.1 Convenções do Page Objects
    7.2 Antes do Page Objects
    7.3 Declarando osPage Objects
  1. Encontrando locators para os elementos
    8.1 Identificando os elementos
    8.2 Estrutura de um locator
    8.3 Declarando os locators em python
  1. Chamadas WebDriver
    9.1 Asterisco como argumento no Find Element
    9.2 Implementação
  1. Repositorio
    10.1 README.md
    10.2 .gitignore
  1. Conclusão
    11.1 O que fica pra depois
  1. Bibliografia


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.

  1. Desinstale a versão atual do virtualenv: “pip uninstall virtualenv
  2. Desinstale a versão atual do pipenv: “pip uninstall pipenv
  3. Quando perguntado sobre prosseguir (y/n) confirme com y
  4. 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.

  1. Vá até o www.duckduckgo.com.
  2. 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

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *