Acabando com matchers ambíguos no Capybara 2.0


Leia em 1 minuto

O Capybara 2.0 saiu há pouco tempo e com ele vieram algumas mudanças que afetam diretamente o modo como devemos referenciar os elementos da página.

É muito comum, por exemplo, termos um link para a página de login no header e no footer. Veja o exemplo abaixo:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Awesome Site!</title>
  </head>

  <body>
    <header>
      <h1>Awesome Site!</h1>
      <a href="/login">Log in</a>
    </header>

    <!-- content -->

    <footer>
      <a href="/login">Log in</a>
    </footer>
  </body>
</html>

Na Capybara 1, você poderia referenciar este link sem ter que definir o contexto. Um simples click_link "Log in" já era suficiente. No Capybara 2.0 isso não é mais possível. Se você tentar fazer isso irá receber uma exceção dizendo que o seu matcher é ambíguo.

Failure/Error: click_link("Log in")
     Capybara::Ambiguous:
       Ambiguous match, found 2 elements matching link "Log in"

Agora é preciso que você seja específico quanto ao elemento que deseja utilizar. Neste caso, a solução mais recomendada é usar o método within, definindo o contexto de seleção do elemento.

within("header") do
  click_link "Log in"
end

Embora esta sugestão funcione, ela traz dois problemas. O primeiro é que você irá, provavelmente, duplicar este código em mais de um lugar e todos nós sabemos que duplicar código quase sempre não é uma boa ideia. O outro problema é que seu teste terá um ruído desnecessário que, dependendo do contexto, pode torná-lo muito mais difícil de ler e entender.

Minha sugestão, neste caso, é criar helpers que irão fazer isso por você. Assim você acaba com os problemas que citei acima.

Primeiro, crie um arquivo chamado “spec/support/feature_helpers.rb”. Neste arquivo nós iremos criar um módulo com helpers que iremos utilizar no grupo de features. Em vez usar o método within, nós iremos usar o método find, que retorna um contexto onde podemos buscar os elementos que precisamos.

module FeatureHelpers
  def main_header
    find("header")
  end
end

RSpec.configure do |config|
  config.include FeatureHelpers, :type => :feature
end

A maior vantagem de se criar um método como esse é que o seletor está totalmente encapsulado. Se você precisar mudar o contexto por algum motivo, basta alterar em um único lugar!

Agora, basta alterar o nosso teste para usar este contexto.

main_header.click_link "Log in"

Você pode usar esta técnica sempre que perceber que está fazendo muitas coisas durante os testes.