Testes automatizados com C# e Selenium – parte 2

E aí! Vamos (finalmente!) escrever aquele teste com Selenium!

Oprah com um microfone gritando e apontando para a platéia

🔙 Caso você não tenha visto a parte 1 desse post, com todas as configurações para fazer esse teste, corre lá!

⚠️ Lembrando: estamos criando um projeto de teste solo e vamos testar algo bem básico; no caso, vamos pesquisar algo no Google e conferir se o primeiro resultado vem preenchido. Também quero recordar que estou usando e passando somente os conhecimentos que estão dentro da minha alçada, ou seja: .NET Framework e IEDriver como navegador para o Selenium.

Bons nomes são TUDO

Caso você ainda não conheça, vou fazer as devidas apresentações: esse aqui é o Clean Code, o livro.

Capa do livro Clean Code, de Robert C. Martin

Eu tenho fé que, um dia, você vai pegá-lo para ler (ou já tenha lido, nem que seja um resumo). Por que estou falando dele aqui? Porque, em determinado capítulo, ele explica a importância dos nomes em TUDO: variáveis, métodos, classes, etc. Sem brincadeira: colocar um nome bem claro no que você está criando é extremamente importante e vai te facilitar muito a vida depois (e de todo mundo que vai mexer no seu código no futuro). Não importa se vai ficar muito longo (depende da linguagem, claro), mas deixe tudo muito bem escrito. Por favorzinho. 🙏

Com os testes, não é diferente: precisamos deixar todos os nomes muito bem explícitos. Aqui, vou adaptar para o nosso contexto as boas práticas de nomenclatura indicadas pela Microsoft.

Vamos começar renomeando a nossa classe de teste. Botão direito na classe e selecione a opção Rename (Renomear)¹. Vamos chamar a classe de BuscaGoogleTest.cs. Você pode colocar outro nome, se preferir.

Quando terminarmos a edição, o Visual Studio vai mostrar um alerta, perguntando se queremos renomear todas as referências ao elemento de código UnitTest1. Pode clicar em Yes (Sim), vai facilitar o nosso trabalho.

Imagem explicativa relativa ao parágrafo acima

Feito isso, vamos abrir nossa classe de teste e começar a escrever nosso código.

Três métodos e suas notações

Logo que a classe de teste é criada e ela se abre para você, é possível ver um começo de código. E nós vamos aproveitá-lo porque a gente não é bobo nem nada.

Antes de mais nada, vou apresentar as quatro principais notações do MSTest que vamos utilizar no nosso teste:

[TestClass] é a notação que demonstra que a classe contém testes automatizados. Se você não marcar a sua classe com essa notação, ela não vai aparecer no Test Explorer (Gerenciador de Testes), por exemplo.

[TestMethod] é a notação que indica que um método de teste.

[TestInitialize] indica o método de inicialização do teste. Esse método vai ser chamado uma vez antes da execução de cada teste da classe (ou seja, cada [TestMethod]).

[TestCleanup] indica o método de finalização do teste. Vai ser chamado depois da execução de cada teste da classe (ou seja, cada [TestMethod]).

Nós já temos a TestClass e um TestMethod criados. Então vamos: 1) criar nossos métodos de inicialização e finalização e 2) renomear nosso método de teste.

[TestClass]
public class BuscaGoogleTest
{
      [TestInitialize]
      public void Initialize()
      {
      }

      [TestMethod]
      public void BuscarSeleniuWebDriver_PrimeiroResultadoEnderecoSiteSelenium()
      {
      }

      [TestCleanup]
      public void CleanUp()
      {
      }
   }

⚠️ Pretendo ir colocando somente códigos pontuais aqui, nesse caso, o da classe; mas obviamente você mantenha o namespace e os using aí do seu lado, tá bem?

Boa, já temos código! Se você abrir o seu Test Explorer (Gerenciador de Testes), já vai conseguir ver que sua classe e seu método de teste já foram devidamente reconhecidos.

Imagem explicativa relativa ao parágrafo acima

⚠️ Caso você não tenha essa aba aberta no seu Visual Studio, vai lá no menu Test > Windows > Text Explorer (Teste > Janelas > Gerenciador de Testes).

Mas ainda precisamos rechear esses métodos. Senão não adianta muita coisa, né? 😅 Vamos começar criando alguns elementos que vamos usar por todo o código e inicializando-os no nosso método correspondente.

IWebDriver e WebDriverWait

Antes do nosso método de inicialização, vamos criar duas variáveis: uma relacionada ao IWebDriver e outra relacionada à um elemento de espera, o WebDriverWait.

[TestClass]
public class BuscaGoogleTest
{
      IWebDriver _driver;
      WebDriverWait _espera;

      [TestInitialize]
      public void Initialize()
      {
      }

      [TestMethod]
      public void BuscarSeleniuWebDriver_PrimeiroResultadoEnderecoSiteSelenium()
      {
      }

      [TestCleanup]
      public void CleanUp()
      {
      }
}

O IWebDriver é a interface pelo qual o usuário vai controlar o browser. E o WebDriverWait vai ser usado quando precisarmos aguardar um determinado elemento aparecer na tela antes de buscá-lo.

Dentro do método Initialize(), nós vamos instanciar essas duas variáveis, para que esse processo aconteça antes da execução de cada teste.

🙋‍♀️ Mas por que dentro do Initialize()? Por que não logo ali, onde as variáveis estão declaradas?

👩‍💻 Quando instanciamos essas variáveis dentro do Initialize(), garantimos que não haverá “lixo” de um teste para o outro, pois será sempre uma instância nova a ser aberta antes da execução de cada teste.

[TestInitialize]
public void Initialize()
{
         _driver = new InternetExplorerDriver(ConfigurationManager.AppSettings["URLDriverIE"]);
         _espera = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
}

Para a interface do IWebDriver, vamos instanciar uma classe relativa ao driver do Internet Explorer. Para isso, precisaremos mandar como parâmetro o endereço onde deixamos o .exe relativo à ele (lembra que o colocamos dentro do App.config na parte um? Vamos recuperá-lo aqui através do ConfigurationManager).

E para o WebDriverWait, vamos precisar da nossa instância do IWebDriver e de um tempo de timeout em formato TimeSpan. Ali, deixei 10 segundos.

Para que as bibliotecas e referências sejam adicionadas à sua classe, é só ir apertando Ctrl + . em cada um dos erros que aparecerem. Em todo caso, até agora temos essas bibliotecas adicionadas:

using System;
using System.Configuration;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using OpenQA.Selenium.Support.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Método Initialize

Por enquanto, já temos duas linhas no método Initialize(). Parece pouco, mas adivinha só? Nosso teste já abre o IE sozinho! Se quiser testar, vai lá no Test Explorer (Gerenciador de Testes) e selecione Run All (Executar Todos).

Concordemos que ainda falta bastante do que nos propomos a desenvolver, mas você já consegue ver a coisa funcionando!

Agora vamos pensar: quando você, como usuário, vai fazer uma consulta no Google, quais são os passos que você dá?

Primeiro você abre um navegador de sua escolha (OK, já temos esse passo implementado) e depois você vai até a URL www.google.com. Vamos fazer isso no nosso método Initialize() então!

[TestInitialize]
public void Initialize()
{
         _driver = new InternetExplorerDriver(ConfigurationManager.AppSettings["URLDriverIE"]);
         _espera = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));

         _driver.Manage().Window.Maximize();
         _driver.Navigate().GoToUrl("https://www.google.com/");
}

O código da linha 7 indica que vamos maximizar a janela do navegador. Isso evita problema com o posicionamento dos campos (não sei explicar porque, mas evita). E o código da linha 8 é o que vai fazer o browser navegar até a URL que queremos.

⚠️ Uma coisa que é importante verificar e que já me deu problemas várias vezes é o zoom do seu navegador. Antes de rodar o teste, especialmente se você fizer algo maior e/ou mais complexo, é sempre abrir o navegador que você vai usar e conferir se o zoom está em 100%. Se não estiver, restaure.

O que eu gosto de fazer também é colocar o endereço que vou testar no App.config. Comigo aconteceu de ter que testar ambientes diferentes do sistema que estavam em URLs diferentes, e estando no App.config eu não precisaria recompilar o programa para ter a alteração implementada. Já vimos como criar uma configuração no App.config e como resgatá-la, mas vou deixar o código aqui para facilitar, OK?

<!-- No App.Config, dentro da tag appSetyings -->
<add key="URLBuscaGoogleTest" value="https://www.google.com/">
//Resgatando o endereço
_driver.Navigate().GoToUrl(ConfigurationManager.AppSettings["URLBuscaGoogleTest"]);

Temos nosso método de inicialização de testes feito! Agora vamos para o nosso método de teste de fato.

Método de teste

🙋‍♀️ Antes de mais nada, por que o nome do método ficou grande daquele jeito (BuscarSeleniuWebDriver_PrimeiroResultadoEnderecoSiteSelenium)?

👩‍💻 De acordo com as boas práticas da Microsoft para nomenclaturas de teste, é bom sempre colocar o cenário que estamos testando e o resultado esperado para aquele cenário. Eles também indicam colocar o método ou funcionalidade que está sendo testada no nome, porém eu preferi indicar este na classe em vez de deixá-lo também no método!

Muito bem, vamos novamente pensar em você, como usuário. Você precisa fazer uma pesquisa no Google, já abriu o navegador e já foi até a página inicial do site. E agora?

Parece um pouco óbvio pensando em como uma pessoa faz, mas para o algoritmo, esse ponto é extremamente importante: você precisa localizar o campo de texto, inserir o que você está buscando, localizar o botão de busca, clicá-lo e verificar se o primeiro resultado é o que você busca (e aqui vamos considerar como primeiro resultado aquele que vem logo após o resultado de propaganda que o Google sempre insere).

Então vamos à isso.

[TestMethod]
public void BuscarSeleniuWebDriver_PrimeiroResultadoEnderecoSiteSelenium()
{
   IWebElement caixaPesquisaGoogle = _driver.FindElement(By.XPath("//input[@title='Pesquisar']"));
   caixaPesquisaGoogle.SendKeys("Selenium WebDriver");

   IWebElement btnPesquisaGoogle = _driver.FindElement(By.XPath("//input[@name='btnK']"));
   btnPesquisaGoogle.SendKeys(Keys.Enter);
         
   IWebElement primeiroResultado = _espera.Until(ExpectedConditions.ElementExists(By.ClassName("rc")));

   Assert.IsNotNull(primeiroResultado.FindElement(By.ClassName("r")).FindElement(By.XPath("//a[@href='https://www.selenium.dev/projects/']")));
}

Vou explicar linha por linha!

Na linha 4, vamos fazer o passo de encontrar o campo de texto relacionado à pesquisa e selecioná-lo. Para nós, como usuário, pode parecer extremamente trivial; afinal de contas, a interface da página inicial do Google é extremamente intuitiva. Mas para o computador, não é.

🙋‍♀️ Mas como é que você sabe que o campo de texto da página inicial do Google tem o título “Pesquisar”?

👩‍💻 Inspecionar elemento é TUDO na vida de quem faz testes com Selenium. Você pode até se guiar pelo código fonte caso esteja testando um sistema que você conheça, mas eu prefiro ir no Inspecionar Elemento para ter certeza dos nomes dos elementos depois de renderizados. É só ir lá, botão direito no elemento que você quer saber nome e selecionar a opção Inspecionar Elemento. O código HTML da caixa de texto principal do Google, por exemplo, é esse aqui:

<input class="gLFyf gsfi" maxlength="2048" name="q" type="text" jsaction="paste:puy29d" aria-autocomplete="both" aria-haspopup="false" autocapitalize="off" autocomplete="off" autocorrect="off" autofocus="" role="combobox" spellcheck="false" title="Pesquisar" value="" aria-label="Pesq." data-ved="0ahUKEwiiz5DAhaDqAhUMyYUKHb3vAa8Q39UDCAQ">

Para a seleção dessa caixa de texto, eu achei o melhor utilizar o XPath, que serve para navegar entre elementos e atributos de um XML. Assim consigo filtrar que, de todos os inputs daquele documento (representado pelo símbolo “//”), eu quero selecionar aquele cujo title é Pesquisar. Fica aqui um bom guia de como usar o XPath com o Selenium.

Na linha 5, através do método SendKeys(), vamos enviar para a caixa de texto já selecionada o valor com a qual queremos preenchê-la. O Selenium vai preencher pra gente, tal qual um usuário digitando muito rápido.

⚠️ Caso você tenha usado o método SendKeys() e ele esteja digitando muito devagar no campo texto, provavelmente você está usando o IEWebDriver de 64 bits. Isso aconteceu com a versão que eu usei (3.14). É só baixar a versão de 32 e substituir e vai ficar tudo bem. Você pode ler mais sobre isso aqui.

Nas próximas duas linhas (7 e 8), vamos seguir a mesma lógica: localizar o botão de busca via XPath (dessa vez via nome) e clicar nele usando o SendKeys(). Esse método é bem versátil: caso você passe uma string como argumento, como fizemos anteriormente, para um elemento que é um campo, ele vai preencher com esse valor. Mas também temos a possibilidade de mandarmos uma “tecla”, representada pela classe Key do Selenium. No caso, vamos mandar a tecla Enter (que é o que geralmente fazemos quando vamos ao Google: digitamos o que precisamos e apertamos Enter).

Opções da classe Key, do Selenium: Alt, ArrowDown, ArrowLeft, etc.
Lista de opções da classe Key

Muito bem, agora estamos na reta final do nosso teste! O que precisamos fazer agora é: selecionar o primeiro resultado e verificar se não é nulo. Parece bobo, mas é um tutorial básico. Você pode refinar as suas verificações depois do jeito que preferir!

Na linha 10 você pode ver como fazer a seleção do resultado. Verifique que ela é diferente das outras seleções que fizemos anteriormente: aqui, estamos usando o elemento de espera. Quando fazemos pesquisas no Google, nem sempre o resultado é tão imediato quanto gostaríamos; sua internet pode estar um pouco instável ou pode simplesmente não ser tão rápida. Então aqui, vamos usar o WebDriverWait que criamos lá em cima.

Basicamente, você está dizendo para o Selenium: aguarda até que o elemento com ClassName “rc” exista para selecioná-lo. Aqui também achei melhor não usar o XPath, porque apesar de querermos algo específico (o primeiro resultado), primeiros precisamos selecionar a div que contém todos os resultados da página (tirando os que são Ads).

E por fim, na linha 12, como todo teste, precisamos colocar a condição de teste. Aqui, estou somente conferindo se o primeiro resultado (que tem como ClassName “r”) é diferente de nulo. O que no caso, é. E aí passa.

Voilá, método de teste feito!

Imagem de um cachorrinho mexendo as patinhas e comemorando

Método CleanUp

Muito bem: você rodou seu teste através do Text Explorer (Gerenciador de Teste) e ele passou. Boa! Mas a aba do navegador continua ali para te assombrar. E se você rodar o teste 20 vezes, vão ficar ali 20 abas abertas. Se você tem memória RAM para isso tudo, ótimo. No caso eu não tenho, então deixo desenvolvido meu método de limpeza.

Vamos supor que você fez a sua consulta, encontrou o resultado e agora não precisa mais daquilo. Quais são os passos que você, como usuário, faz? Fecha a aba/navegador, certo? Então vamos colocar isso no seu método de CleanUp.

[TestCleanup]
public void CleanUp()
{
         _driver.Close();
         _driver.Quit();
}

Prontinho! Como dito lá em cima, esse método tem a marcação de TestCleanup, então ele vai ser rodado uma vez depois de cada teste. Com isso, automaticamente seu navegador vai ser fechado e você não precisa de preocupar com isso depois. Afinal de contas, é para isso que queremos testes automatizados, certo? Para facilitar a nossa vida! 😊

Siga praticando!

Ufa, terminamos! Já peço desculpas por esse post gigante 😅 Eu quis desde o princípio escrever esse tutorial de uma forma bem completa, que pudesse ajudar desde uma pessoa mais senior até quem chegou agora no mundo da programação.

Ah: sigam brincando com Selenium! Eu, pessoalmente, o acho bem divertido e também uma mão na roda na hora de testar sistemas legados.

Espero que gostem e qualquer feedback é muito bem vindo!

O cachorro Doug, do filme Up, olhando para a tela e abanando o rabo.

EDIT: Deixei em um repositório do GitHub o projeto completo! Ele engloba as partes 1 e 2 desse tutorial, assim como o tutorial de testes de carga!


¹ Meu Visual Studio está em inglês e estou traduzindo no freestyle.

2 comentários em “Testes automatizados com C# e Selenium – parte 2”

Deixe uma resposta

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

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.