Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby
Code

Objetos Ruby de Página para Conhecedores de Capybara

by
Length:LongLanguages:

Portuguese (Português) translation by Thiago Oliveira (you can also view the original English article)

Final product image
What You'll Be Creating

O que são Objetos de Página?

Vou te dar o resumo primeiro. É um padrão de design para encapsular markup e interações com páginas — especificamente para refatorar seus testes de funcionalidade. É a combinação de duas técnicas muito comuns de refatoração: Extrair classe e Extrair método — que não precisam acontecer ao mesmo tempo por que você pode gradualmente trabalhar para extrair uma classe completa por meio de um novo Objeto de Página.

Essa técnica permite que você escreva testes de funcionalidade de alto nível que são muito expressivos e DRY. De certa maneira, eles são testes de aceitação com linguagem de aplicação. Você pode perguntar, testes escritos com Capybara já não são de alto nível e expressivos? Sem dúvida, para desenvolvedores que escrevem código diariamente, testes em Capybara são bem legíveis. Eles são DRY automaticamente? Não muito — na verdade, certamente não!

Quando você olha para esses exemplos de testes de funcionalidades, onde você vê oportunidades de torná-los mais legíveis, e como você poderia extrair informações para evitar duplicação? Também, isso é suficientemente de alto nível para fácil modelação de histórias de usuários e para stakeholders não técnicos entenderem?

Na minha mente, existem algumas maneiras de melhorar isso e fazer todo mundo feliz — desenvolvedores que podem evitar lidar com os detalhes de interagir com o DOM enquanto aplicando OOP, e outros membros da equipe que não escrevem código não tendo problemas pulando entre histórias de usuários e esses testes. O último ponto é legal de se ter, com certeza, mas os benefícios mais importantes vêm majoritariamente de tornar os seus testes que interagem com o DOM mais robustos.

Encapsulação é um conceito chave com Objetos de Página. Quando você escreve seus testes de funcionalidade, você se beneficiará de uma estratégia para extrair o comportamento que está se desenrolando por um fluxo do teste. Para código de qualidade, você quer capturar as interações com grupos particulares de elementos nas suas páginas — especialmente se você se depara com padrões que se repetem. Ao longo que a sua aplicação cresce, você quer / precisa de um meio que evite espalhar aquela lógica por toda parte em seus testes.

“Bem isso não é exagero? Capybara é bem legível!” você diz?

Pergunte a si mesmo: Porque você não teria todos os detalhes de implementação de HTML em um lugar enquanto tendo testes mais estáveis? Por que não deveriam testes de interação com a UI ter a mesma qualidade que testes para o código da aplicação? Você realmente quer parar aí?

Devido à mudanças diárias, seu código Capybara é vulnerável quando espalhado por toda a parte — isso introduz possíveis pontos de quebra. Vamos dizer que um designer quer mudar o texto em um botão. Não é uma grande coisa, certo? Mas você quer se adaptar àquela mudança em um wrapper central para aquele elemento nos seus testes, ou você prefere fazer isso por todo lugar? Também pensei assim!

Há muitas possíveis refatorações para seus testes de funcionalidade, mas Objetos de Página oferecem abstrações mais limpas para encapsular comportamentos que estão expostos aos usuários para páginas ou fluxos mais complexos. Entretanto, você não precisa simular toda(s) a(s) página(s) — foque nas partes essenciais que são necessárias para o fluxo do usuário. Não precisa exagerar!

Testes de Aceitação / Testes de Funcionalidade

Antes de avançarmos para o coração da questão, gostaria de dar um passo atrás para pessoas que são novas para todo o negócio de testes e esclarecer alguns dos jargões que são importantes nesse contexto. Pessoas mais familiares com TDD não vão perder muito se pularem adiante.

Sobre o que estamos falando aqui? Testes de aceitação geralmente vêm num estágio mais tarde dos projetos para avaliar se você tem construído algo de valor para seus usuários, gerente de produto, ou outro stakeholder. Esses testes são geralmente rodados por clientes ou seus usuários. É como uma verificação se os requerimentos estão sendo alcançados ou não. Há algo como uma pirâmide para todos os tipos de camadas de testes, e testes de aceitação estão bem próximos topo. Por que esse processo geralmente inclui pessoal não técnico, uma linguagem de alto nível para escrever esses testes é um recurso valioso para a comunicação de ambos os lados.

Testes de funcionalidade, por outro lado, estão um pouco mais baixo na cadeia alimentar de testes. Bem mais alto nível que testes unitários, que focam em detalhes técnicos e lógica de negócio dos seus modelos, testes de funcionalidade descrevem fluxos em ou entre suas páginas.

Ferramentas como Capybara te ajudam a evitar fazer isso manualmente, significando que você raramente tem que abrir seu browser para testar coisas manualmente. Com esses tipos de testes, nós gostamos de automatizar essas tarefas tanto quanto possível e fazer um test-drive da interação por meio do browser enquanto escrevendo asserções contra páginas. A propósito, você não usa get, put, post ou delete como você faz com testes de requisição.

Testes de funcionalidade são muito similar a testes de aceitação — algumas vezes eu sinto que as diferenças são muito difusas para realmente se importar com a terminologia. Você escreve testes que exercitam toda a sua aplicação, o que geralmente envolve um fluxo de ações de usuários de múltiplos passos. Esses testes de interações mostram se seus componentes trabalham em harmonia quando eles são reunidos.

Na terra do Ruby, eles são o maior protagonista quando estamos lidando com Objetos de Página. Testes de funcionalidades são eles mesmos já bem expressivos, mas eles podem ser otimizados e limpados por meio da extração de seus dados, comportamento e markup em uma classe ou classes separadas.

Espero que esclarecendo essa terminologia obscura vai ajudá-lo a ver que ter Objetos de Página é algo como fazer testes de nível de aceitação enquanto se escreve testes de funcionalidade.

Capybara

Talvez devamos passar por isso bem rápido também. Essa biblioteca descreve a si mesma como um "Framework de testes de aceitação para aplicações web". Você pode simular interações de usuários com suas páginas por meio de uma linguagem de domínio específico muito poderosa e conveniente. Na minha opinião pessoal, RSpec junto com Capybara oferece a melhor maneira de escrever seus testes de funcionalidade no momento. Ele o permite visitar páginas, preencher formulários, clicar em links e botões, e olhar o markup em suas páginas, e você pode facilmente combinar todo tipo desses comandos para interagir com suas páginas pelo seus testes.

Você basicamente pode evitar abrir o browser você mesmo para testar essas coisas manualmente a maioria do tempo — o que não é só menos elegante mas também consome bem mais tempo e é mais propenso a erros. Sem essa ferramenta, o processo de "em-teste" — você leva seu código de testes de alto nível até os seus testes de nível unitário — seria bem mais doloroso e possivelmente portanto mais negligenciado.

Em outras palavras, você começa escrevendo esses testes de funcionalidades que são baseados nas suas histórias de usuários, e daí você desce até à toca do coelho até que seus testes unitários provenham a cobertura que seus testes de funcionalidade precisavam. Depois disso, quando seus testes estiverem verde é claro, o jogo começa novamente e você volta para cima para continuar com um novo teste de funcionalidade. 

Como?

Vamos olhar dois exemplos simples de testes de funcionalidade que deixam M criar missões secretas as quais podem então serem completadas.

No markup, você tem uma lista de missões, e uma conclusão de sucesso cria a classe adicional completed no li da missão específica. Coisa simples, certo? Como uma primeira aproximação, comecei com pequenas, muito comuns refatorações que extraem comportamento comum para métodos.

spec/features/m_creates_a_mission_spec.rb

spec/features/agent_completes_a_mission_spec.rb

Mesmo existindo outras maneiras, é claro, de lidar com coisas como sign_in_ascreate_classified_mission_named e por aí vai, é fácil ver o quão rápido essas coisas podem começar a ficarem ruins e acumularem.

Testes relacionados a UI geralmente não recebem o tratamento OO que precisam / merecem, penso eu. Eles têm a reputação de não valerem tanto a pena, e os desenvolvedores é claro não têm muita afeição por vezes em que têm que mexer muito em coisas de markup. No meu pensar, isso faz com que seja ainda mais importante deixar esses testes mais DRY e tornar divertido lidar com eles jogando neles algumas classes Ruby.

Vamos fazer um pequeno truque de mágica onde eu escondo a implementação do Objeto de Página por enquanto e só mostro a você o resultado final aplicado aos testes de funcionalidade acima:

spec/features/m_creates_a_mission_spec.rb

spec/features/agent_completes_a_mission_spec.rb

Não ficou tão ruim, hein? Você basicamente cria métodos wrapper expressivos nos seus Objetos de Página que deixam você lidar com conceitos de alto nível, ao invés de mexer em todo lugar com os intestinos do seu markup toda hora. Seus métodos extraídos fazem esse tipo de trabalho sujo agora, e dessa maneira shotgun surgery não é mais seu problema.

Colocando de forma diferente, você encapsula a maioria do ruidoso, embrenhado código que interage com o DOM. Tenho que dizer, entretanto, que algumas vezes métodos inteligentemente extraídos nos seus testes de funcionalidade são suficiente e lêem um pouco melhor desde que você possa evitar lidar com instâncias de Objetos de Página. De qualquer maneira, vamos dar uma olhada na implementação:

specs/support/features/pages/missions.rb

O que você vê é um simples velho objeto Ruby — Objetos de Página são em essência classes muito simples. Normalmente você não instancia Objetos de Página com dados (quando a necessidade ocorre, é claro, você pode) e você cria a maioria das vezes uma linguagem por meio da API que um usuário ou um stakeholder não técnico em um time possa usar. Quando você pensa em nomear seus métodos, acho que é um bom conselho se perguntar: Como um usuário descreveria o fluxo ou a ação tomada?

Eu deveria talvez adicionar que sem incluir o Capybara, a música para bem rápido.

Você provavelmente imagina como esses matchers customizados funcionam:

RSpec gera esses matchers customizados baseado em métodos predicados nos seus Objetos de Página. RSpec os converte removendo o ? e troca has por have. Bum, matchers do zero sem muita bagunça! Um pouco mágico, admito, mas o tipo bom de magia, eu diria.

Desde que colocamos nosso Objeto de Página em specs/support/features/pages/missions.rb, você também tem que ter certeza de que o seguinte não está comentado em spec/rails_helper.rb.

Se você cair em um NameError com um uninitialized constant Pages, você saberá o que fazer.

Se você está curioso sobre o que aconteceu com o método sign_in_as, o extraí para um módulo em spec/support/sign_in_helper.rb e disse ao RSpec para incluir esse módulo. Isso não tem nada a ver com Objetos de Página diretamente — simplesmente faz mais sentido guardar funcionalidades de teste como sign in de uma maneira mais acessível globalmente do que por meio de um Objeto de Página.

spec/support/sign_in_helper.rb

E você precisa deixar o RSpec saber que você quer acessar esse módulo helper:

spec/spec_helper.rb

Além de tudo, é fácil ver que tivemos sucesso em esconder as especificidades do Capybara — como encontrar elementos, clicar em links, etc. Podemos agora focar na funcionalidade e menos na estrutura do markup, que agora está encapsulada em um Objeto de Página — a estrutura do DOM deve ser a menor das suas preocupações quando você testa algo tão alto nível como testes de funcionalidade.

Atenção!

Coisas de inicialização como fábrica de dados pertencem a testes e não ao Objeto de Página. Também, asserções são provavelmente melhor colocadas fora dos seus Objetos de Página para conseguir uma separação de interesses.

Existem duas perspectivas diferentes sobre o tópico. Defensores de colocar asserções em Objetos de Página dizem que isso ajuda a evitar a duplicação de asserções. Você pode prover mensagens de erro melhores e conseguir um melhor estilo 'Conte, Não Pergunte'. Por outro lado, defensores de Objetos de Página sem asserções argumentam que é melhor não misturar responsabilidades. Prover acesso à dados de página e lógica de asserção são dois interesses separados e levam a Objetos de Página inchados quando misturados. A responsabilidade de Objetos de Página é acessar o estado das páginas, e lógica de asserção pertence aos testes.

Tipos de Objetos de Página

Components representam as menores unidades e são mais focados — como um objeto de formulário, por exemplo.

Pages combinam mais desses componentes e são abstrações de uma página completa.

Experiences, como você já deve ter suposto, cobrem todo o fluxo entre potencialmente muitas páginas diferentes. Eles são mais alto nível. Eles focam no fluxo das experiências de usuário enquanto interagem com várias páginas. Um fluxo de pagamento que tenha vários passos é um bom exemplo para pensar sobre isso.

Quando & Porquê?

É uma boa ideia aplicar esse padrão de design um pouco mais tarde no ciclo de vida de um projeto — quando você acumulou um pouco de complexidade nos seus testes de funcionalidade e quando você pode identificar padrões que se repetem como estruturas de DOM, métodos extraídos e outros atributos comuns que são consistentes em suas páginas.

Então você provavelmente não deveria começar a escrever Objetos de Página desde o início. Você chega nessas refatorações gradualmente quando a complexidade e tamanho de sua aplicação / testes cresce. Duplicações e refatorações que precisam de um lar melhor por meio de Objetos de Página serão mais fáceis de se identificar com o tempo.

Minha recomendação é começar extraindo métodos nos seus testes de funcionalidade localmente. Uma vez que eles atinjam uma quantidade crítica, eles vão parecer candidatos óbvios para uma extração adicional, e a maioria deles vai provavelmente se encaixar no perfil de Objetos de Página. Comece pequeno, porque otimização pré matura deixa marcas de bites desagradáveis!

Ideias Finais

Objetos de Página provêm a oportunidade de escrever testes mais claros que são mais legíveis e são além de tudo mais expressivos por que eles são mais alto nível. Além disso, eles oferecem uma bela abstração para todos que gostam de escrever código OO. Eles escondem especificidades do DOM e também permitem que você tenha métodos privados que façam o trabalho sujo enquanto não expostos para a API pública. Métodos extraídos nos seus testes de funcionalidade não oferecem tal luxo. A API de Objetos de Página não precisa compartilhar os detalhes complicados do Capybara.

Para todos os cenários quando projetando mudanças de implementação, suas descrições de como sua aplicação deve funcionar não precisam mudar quando você usa Objetos de Página — seus testes de funcionalidade são mais focados em interações no nível do usuário e não ligam muito para as especificidades das implementações do DOM. Desde que mudança é inevitável, Objetos de Página tornam-se críticos quando aplicações crescem e também ajudam a entender quando o puro tamanho da aplicação significa um aumento drástico de complexidade.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.