Iniciante: Longa Vida ao PhantomJS - Vamos Usar Chrome Headless Agora

PT | EN
31 de outubro de 2017 · 💬 Participe da Discussão

Se você faz Feature Specs — o processo de subir um servidor real da aplicação e um browser headless real para testar funcionalidades do ponto de vista do usuário — então você conhece o Capybara e um dos seus drivers mais famosos, o Poltergeist. O Poltergeist encapsula o PhantomJS, que é um browser headless baseado em WebKit bastante conhecido.

Só que o WebKit é famoso por ser muito complicado de lidar. Então dá pra imaginar o pesadelo que é manter o PhantomJS, que é basicamente manter um browser completo como Chrome ou Safari.

Não é de espantar que, quando o time do Chrome anunciou a disponibilidade do Chrome Driver, o mantenedor do PhantomJS decidiu se afastar do projeto.

Se você conhece os contribuidores do PhantomJS, agradeça a eles — o projeto nos ajudou a construir funcionalidades de usuário mais sólidas por anos.

Dito isso, não precisa se preocupar. É perfeitamente possível substituir Poltergeist/PhantomJS por Selenium WebDriver/Chrome Driver no seu setup de RSpec/Capybara.

Meu amigo Lucas Caton escreveu sobre isso em junho deste ano. Vale acompanhar o blog dele também.

Se você usa Linux com o browser Chromium, não precisa instalar nada — o Chrome Driver já vem com o Chromium. Caso contrário, instale os pacotes adequados para o seu sistema operacional. Por exemplo: brew install chromedriver no OS X, ou pacaur -S chromedriver no Arch Linux se preferir não ter o Chromium instalado. No Ubuntu, talvez seja necessário ajustar seu PATH.

Regra geral: instale o Chromium.

No meu caso, as mudanças foram as seguintes:

# Gemfile
- gem "poltergeist"
+ gem "selenium-webdriver"
+ gem "rspec-retry"

Depois, na configuração do Capybara:

Capybara.server = :puma # Até o setup funcionar
Capybara.server = :puma, { Silent: true } # Para limpar o output dos testes

- Capybara.register_driver :poltergeist do |app|
-   options = {
-     timeout: 3.minutes,
-     phantomjs_options: [
-       '--proxy-type=none',
-       '--load-images=no',
-       '--ignore-ssl-errors=yes',
-       '--ssl-protocol=any',
-       '--web-security=false'
-     ]
-   }
-   Capybara::Poltergeist::Driver.new(app, options)
- end
- Capybara.javascript_driver = :poltergeist

+ Capybara.register_driver :chrome do |app|
+   capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
+     chromeOptions: {
+       args: %w[ no-sandbox headless disable-popup-blocking disable-gpu window-size=1280,1024]
+     }
+   )
+ 
+   Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: capabilities)
+ end
+ 
+ Capybara::Screenshot.register_driver :chrome do |driver, path|
+   driver.save_screenshot(path)
+ end
+ 
+ Capybara.javascript_driver = :chrome

Capybara.default_max_wait_time = 5 # aumente esse timeout se sua aplicação for pesada para carregar

Em feature specs, às vezes o próprio Rails demora um pouco para subir, compilar assets e tal, e o primeiro spec pode dar timeout. Para evitar falha na execução dos testes, recomendo adicionar a gem rspec-retry como fiz acima, e incluir o seguinte no seu spec/rails_helper.rb:

require 'rspec/retry'

RSpec.configure do |config|
  ...
  # mostra o status de retry no processo do spec
  config.verbose_retry = true
  # Tenta duas vezes (repete uma vez)
  config.default_retry_count = 2
  # Faz retry apenas quando o Selenium lança Net::ReadTimeout
  config.exceptions_to_retry = [Net::ReadTimeout]
  ...
end

E é isso. Não precisei alterar nenhum dos meus feature specs e todos rodaram perfeitamente. Parabéns às equipes que mantêm o Capybara e o Selenium-WebDriver por suportarem essa mudança.

Se você também desenvolve em Node.js, provavelmente já usou algo como o Casper, que dizem suportar Chrome Headless também. Mas já que estamos no assunto, vale muito conferir o Puppeteer, da própria equipe do Google. É uma biblioteca baseada em Promises onde você escreve código assim:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();

Então sim, o Chrome Headless parece uma ótima opção — afinal, a maioria dos usuários já usa o Chrome, o que significa que teremos feature specs mais confiáveis e ferramentas de web crawling mais robustas.