Migrando do RC para WebDriver

Como migrar para o Selenium WebDriver

Uma pergunta comum ao adotar o Selenium 2 é qual é a coisa certa a fazer ao adicionar novos testes a um conjunto existente de testes? Usuários que são novos no framework podem começar usando as novas APIs WebDriver para escrever seus testes. Mas e os usuários que já possuem suítes de testes existentes? Este guia é projetado para demonstrar como migrar seus testes existentes para as novas APIs, permitindo que todos os novos testes sejam escritos usando os novos recursos oferecidos pelo WebDriver.

O método apresentado aqui descreve uma migração gradativa para as APIs WebDriver sem precisar refazer tudo em um push massivo. Isso significa que você pode permitir mais tempo para migrar seus testes existentes, que pode tornar mais fácil para você decidir onde investir seu esforço.

Este guia foi escrito em Java, porque tem o melhor suporte para fazer a migração. À medida que fornecemos ferramentas melhores para outras linguagens, este guia deve ser expandido para incluir essas linguagens.

Porque migrar para o WebDriver

Mover um conjunto de testes de uma API para outra requer uma enorme quantidade de esforço. Por que você e sua equipe considerariam fazer essa mudança? Aqui estão alguns motivos pelos quais você deve considerar a migração de seus testes Selenium para usar o WebDriver.

  • API menor e compacta. A API do WebDriver é mais orientada a objetos do que o Selenium RC API original. Isso pode facilitar o trabalho.
  • Melhor emulação das interações do usuário. Sempre que possível, o WebDriver faz uso de eventos nativos para interagir com uma página da web. Imita melhor a maneira como seus usuários trabalham com seu site e aplicativos. Além do que, o WebDriver oferece APIs de interações de usuário avançadas que permitem que você modele interações complexas com seu site.
  • Suporte de fornecedores de navegadores. Opera, Mozilla e Google são todos participantes ativos do desenvolvimento do WebDriver, e cada um tem engenheiros trabalhando para melhorar a estrutura. Frequentemente, isso significa que o suporte para WebDriver está embutido no próprio navegador: seus testes são executados tão rápidos e estáveis quanto possível.

Antes de começar

A fim de tornar o processo de migração o mais indolor possível, certifique-se de que todos os seus testes sejam executados corretamente com a versão mais recente do Selenium. Isso pode parecer óbvio, mas é melhor que seja dito!

Começando

A primeira etapa ao iniciar a migração é mudar a forma como você obtém sua instância Selenium. Ao usar Selenium RC, isso é feito assim:

Selenium selenium = new DefaultSelenium("localhost", 4444, "*firefox", "http://www.yoursite.com");
selenium.start();

Isso deve ser substituído assim:

WebDriver driver = new FirefoxDriver();
Selenium selenium = new WebDriverBackedSelenium(driver, "http://www.yoursite.com");

Próximos passos

Depois que seus testes forem executados sem erros, a próxima etapa é migrar o código de teste real para usar as APIs WebDriver. Dependendo de quão bem você abstrair o seu código, pode ser um processo curto ou longo. Em ambos os casos, a abordagem é a mesma e pode ser resumida simplesmente: modifique o código para usar a nova API quando for editá-lo.

Se você precisar extrair a implementação WebDriver subjacente da instância Selenium, você pode simplesmente fazer um cast para WrapsDriver:

WebDriver driver = ((WrapsDriver) selenium).getWrappedDriver();

Isso permite que você continue passando a instância Selenium como normal, mas desembrulhar a instância do WebDriver conforme necessário.

Em algum ponto, sua base de código usará principalmente as APIs mais recentes. Neste ponto, você pode inverter o relacionamento, usando WebDriver em tudo e instanciar uma instância do Selenium sob demanda:

Selenium selenium = new WebDriverBackedSelenium(driver, baseUrl);

Problemas comuns

Felizmente, você não é a primeira pessoa a passar por essa migração, então, aqui estão alguns problemas comuns que outras pessoas viram e como resolvê-los.

Clicar e digitar são mais completos

Um padrão comum em um teste de Selenium RC é ver algo como:

selenium.type("name", "exciting tex");
selenium.keyDown("name", "t");
selenium.keyPress("name", "t");
selenium.keyUp("name", "t");

Isso se baseia no fato de que o “tipo” simplesmente substitui o conteúdo do elemento identificado sem também disparar todos os eventos que normalmente seriam disparados se um usuário interagir com a página. As invocações diretas finais de “key*” faz com que os manipuladores JS sejam acionados conforme o esperado.

Ao usar o WebDriverBackedSelenium, o resultado do preenchimento do campo do formulário seria “exciting texttt”: não o que você esperaria! O motivo disso é que o WebDriver emula com mais precisão o comportamento do usuário, e assim terá disparado eventos o tempo todo.

Esse mesmo fato às vezes pode fazer com que o carregamento da página seja disparado antes do que aconteceria em um teste de Selenium 1. Você pode dizer que isso aconteceu se uma “StaleElementException” é lançada pelo WebDriver.

WaitForPageToLoad retorna muito cedo

Descobrir quando o carregamento de uma página está completo é uma tarefa complicada. Queremos dizer “quando o evento de carregamento dispara”, “quando todas as solicitações AJAX são concluídas”, “quando não há tráfego de rede “,” quando document.readyState mudou” ou outra coisa completamente diferente?

WebDriver tenta simular o comportamento original do Selenium, mas isso não sempre funciona perfeitamente por vários motivos. O motivo mais comum é que é difícil dizer a diferença entre um carregamento de página que ainda não começou e um carregamento da página concluído entre as chamadas de método. Isso às vezes significa que o controle é devolvido ao seu teste antes que a página termine (ou mesmo comece!) o carregamento.

A solução para isso é esperar por algo específico. Normalmente, isso pode ser o elemento com o qual deseja interagir a seguir, ou para alguma variável Javascript a ser definida com um valor específico. Um exemplo seria:

Wait<WebDriver> wait = new WebDriverWait(driver, Duration.ofSeconds(30));
WebElement element= wait.until(visibilityOfElementLocated(By.id("some_id")));

Onde “visibilityOfElementLocated” é implementado como:

public ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator) {
  return new ExpectedCondition<WebElement>() {
    public WebElement apply(WebDriver driver) {
      WebElement toReturn = driver.findElement(locator);
      if (toReturn.isDisplayed()) {
        return toReturn;
      }
      return null;
    }
  };
}

Isso pode parecer complexo, mas é quase todo um código padrão. O único interessante é que a “condição esperada” será avaliada repetidamente até que o método “apply” retorne algo que não seja “null” nem Boolean.FALSE.

Claro, adicionar todas essas chamadas de “wait” pode confundir seu código. E se esse é o caso, e suas necessidades são simples, considere usar as esperas implícitas:

driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

Ao fazer isso, toda vez que um elemento é localizado, se o elemento não estiver presente, o local é tentado novamente até que esteja presente ou até 30 segundos passados.

Encontrar por seletores XPath ou CSS nem sempre funciona, mas funciona no Selenium 1

No Selenium 1, era comum para o xpath usar uma biblioteca agrupada em vez de os recursos do próprio navegador. WebDriver sempre usará os métodos nativos do navegador, a menos que não haja alternativa. Isso significa que expressões xpath complexas podem falhar em alguns navegadores.

Os seletores CSS no Selenium 1 foram implementados usando a biblioteca Sizzle. Esta biblioteca implementa um superconjunto da CSS Selector Spec, e nem sempre é claro onde você cruzou a linha. Se você estiver usando o WebDriverBackedSelenium e usar um Localizador Sizzle em vez de um Seletor CSS para encontrar elementos, um aviso ser registrado no console. Vale a pena procurar por eles, particularmente se os testes estão falhando por não ser capaz de encontrar os elementos.

Não há nenhum Browserbot

O Selenium RC era baseado no Selenium Core e, portanto, quando você executava Javascript, você podia acessar bits do Selenium Core para tornar as coisas mais fáceis. Como o WebDriver não é baseado no Selenium Core, isso não é mais possível. Como você pode saber se está usando Selenium Core? Simples! Basta olhar para ver se o seu “getEval” ou chamadas semelhantes usam “selenium” ou “browserbot” no Javascript avaliado.

Você pode estar usando o browserbot para obter um identificador para a janela atual ou documento do teste. Felizmente, o WebDriver sempre avalia JS no contexto da janela atual, então você pode usar “window” ou “document” diretamente.

Como alternativa, você pode usar o browserbot para localizar elementos. No WebDriver, o idioma para fazer isso é primeiro localizar o elemento, e então passe isso como um argumento para o Javascript. Portanto:

String name = selenium.getEval(
    "selenium.browserbot.findElement('id=foo', browserbot.getCurrentWindow()).tagName");

se torna:

WebElement element = driver.findElement(By.id("foo"));
String name = (String) ((JavascriptExecutor) driver).executeScript(
    "return arguments[0].tagName", element);

Observe como a variável “element” passada aparece como o primeiro item na array de “arguments” padrão do JS.

A execução de Javascript não retorna nada

O JavascriptExecutor do WebDriver envolverá todo o JS e o avaliará como uma expressão anônima. Isso significa que você precisa usar a palavra-chave “return”:

String title = selenium.getEval("browserbot.getCurrentWindow().document.title");

se torna:

((JavascriptExecutor) driver).executeScript("return document.title;");
Última modificação January 10, 2022: More wiki (#907) [deploy site] (adcf706a1ad)