Concrete Logo
Hamburger button

Boas práticas de Cucumber e Calabash – Parte 2

  • Blog
  • 28 de Setembro de 2015
Share

Introdução

Quando comecei a trabalhar com projetos de QA aqui na Concrete, eu passei a me perguntar quais seriam as melhores práticas para a automatização dos testes usando Calabash. Uma boa parte das dicas com as minhas descobertas eu publiquei nesse post aqui, há algum tempo. Hoje, vou falar um pouco mais sobre o que eu tenho encontrado em algumas publicações na web de pessoas mais experientes na área. Em especial, esse post do Clemens Helm me motivou a escrever uma segunda parte sobre o assunto.

E, por incrível que pareça, foram as discussões nos comentários que eu considerei mais úteis: duas formas de implementar os seus testes automatizados no Calabash. Duas visões antagônicas que não necessariamente estão erradas, mas que cabe um levantamento dos prós e dos contras de cada abordagem antes que alguém decida optar por uma delas. Então vamos lá!

Como sabemos, quando utilizamos o Cucumber, os projetos de testes normalmente se dividem em duas camadas:

image01

Quando trabalhamos com Calabash uma terceira camada se faz necessária, a de Screens:

image03

Em alguns casos, quando os steps das diferentes plataformas são muito diferentes, eles costumam ser definidos de maneira individualizada para cada plataforma. Assim, a arquitetura acima pode ficar assim:

image02

Essa é uma maneira de estruturar o seu projeto de testes para o Calabash quando estamos usando iOS e Android. Nesse post, vamos abordar duas maneiras de como implementar os seus testes em todas as camadas acima citadas. Mas, antes, vamos deixar claras algumas nomenclaturas:

– Vamos chamar a primeira camada como a que contém seus arquivos Feature do projeto.
– A segunda se refere aos arquivos de Steps.
– Já a terceira é referente à implementação das classes Screen.

Outro termo que vamos utilizar aqui é “alto nível”, que se refere ao entendimento do usuário, e “baixo nível”, relacionado ao entendimento de implementação do desenvolvedor.

Depois dessa pequena contextualização, vamos para a discussão das duas possibilidades de implementação das camadas dos nossos projetos de testes com Calabash.

Padrão 1: Detalhamento no alto nível

Conforme indica o título, essa corrente de pensamento acredita que é uma boa prática de desenvolvimento para Cucumber – arquitetura em que o Calabash se baseia – implementar os passos de arquivo .feature da maneira mais detalhada possível.

Por exemplo, suponhamos que o programador de QA resolva testar a funcionalidade de fazer login do seu aplicativo. Um possível arquivo .feature, pertencente a uma hierarquia de alto nível, sob essa perspectiva, seria um escrito da seguinte forma:

O arquivo de steps, correspondente à segunda hierarquia de abstração, fica da seguinte forma:

O primeiro step atribui a classe TelaLogin a uma variável. A classe TelaLogin pertence à terceira camada e representa a tela de login, tanto para Android quanto para iOS e com a mesma interface, porém com diferentes implementações para cada sistema operacional.

A implementação da classe de tela login de Android, referente à terceira camada, poderia ficar assim:

Observe que, para cada gesto do usuário, definido na linguagem de alto nível do Gherkin na camada de features, existe um método correspondente nas classes de Screens, que corresponde à abstração da tela e às possíveis ações sobre ela.

Claro que, se formos implementar os testes para uma funcionalidade que requer muitos passos, é possível agrupá-los sob a definição de um novo passo, por meio da seguinte sintaxe:

Você pode ver mais detalhes sobre esse agrupamento neste link aqui.

Esse recurso vai contra o que é mostrado na outra corrente de pensamento, que evita fazer agrupamento de steps e delega todos os detalhes para a 3ª camada que mencionamos no início do post, de implementação dos métodos da tela.

Observe que o agrupamento é formado por steps já utilizados e definidos pelo desenvolvedor.
Prefira utilizar agrupamentos quando você quiser resumir um conjunto de passos que já foram testados apropriadamente em outros cenários e que não sejam o foco de atenção para um cenário específico. Com essa abordagem, o custo é somente o de manutenção: caso um dia seja necessário modificar o enunciado de um step, será necessário atualizar onde quer que ele tenha sido utilizado.

Vantagens:
– Legibilidade para o desenvolvedor – é mais fácil saber o que cada passo representa e como é feita a correspondente implementação em cada camada;
– Maior transparência – as partes interessadas do projeto poderão ter mais detalhes, em linguagem natural, de como seriam as ações do usuário para cada cenário de teste;
– Reutilização de código – um step pode ser facilmente reutilizado. Basta fazer referência a ele em qualquer agrupamento de steps e deixá-lo o mais modularizado possível.

Desvantagens:
– Muitos steps entediantes – exagerar na quantidade de passos para um cenário de teste pode ser desestimulante para leitura. Neste caso, é válido o agrupamento, conforme discutido;
– Custo de manutenção – se um mesmo passo for utilizado em vários agrupamentos, haverá o custo de manter as referências a esse passo atualizadas, caso seu enunciado venha a ser modificado no futuro. Sugestão: agrupe os passos quando tiver certeza que não irá mais modificar o enunciado dos subpassos, para que não precise atualizá-los sempre que estes precisarem sofrer alterações. Normalmente isso requer experiência do implementador para saber como deixar um step com um enunciado que seja pouco modificado ao longo do projeto.

Agora vamos analisar outra possibilidade de implementação das camadas do projeto de testes com Calabash.

Padrão 2: detalhamento em baixo nível

Conforme já é de se esperar, nesse padrão os arquivos .feature, correspondentes à hierarquia de alto nível, são bem sucintos. Os detalhes ficarão especificados em baixo nível, mais especialmente na terceira camada – de Screens. Ou seja, os subpassos relacionados a um passo mais sucinto serão implementados nas outras camadas, principalmente na terceira.
Neste caso, os métodos das classes desta última camada ficariam eventualmente grandes, pois representariam todos os passos de um único step, resumindo uma grande quantidade de subpassos.

Nesse modelo, não se recomenda sequer o agrupamento de steps para auxiliar. Vamos retomar o mesmo exemplo da seção anterior. Nossa .feature ficaria assim:

A definição dos steps poderia ficar algo do tipo:

Nesse caso, utilizamos um hash que demos o nome de “USERS”, externo ao arquivo de steps, para armazenar os dados que queremos averiguar. Na definição das telas, veríamos a maior diferença em relação ao método anterior: removemos os métodos “digitar_login”, “digitar_senha” e “clicar_logar” e criamos um método mais completo, o “fazer_login”, que englobaria todos eles:

Observe que este é um caso simples, porém pode haver passos que requeiram muito mais subpassos ou verificações implícitos, nos quais as funções podem ficar grandes e complexas.
Essa abordagem é a utilizada pela Xamarin, cujo exemplo mais completo pode ser achado neste repositório.

Vantagens:
– Garante total desacoplamento entre a implementação das classes de tela e os passos correspondentes. Por exemplo, se fizermos login no iOS usando um procedimento X que é totalmente diferente do procedimento Y do Android, a primeira (features) e a segunda (steps) camadas não precisam se preocupar tanto com a diferença semântica entre plataformas. Somente a terceira camada precisaria tratar essas diferenças, naturalmente, por ser a camada relacionada às especificidades de implementação de cada plataforma;
– Facilidade em trabalhar quando existem muitas funcionalidades que agem de maneiras completamente distintas entre as duas plataformas: quando um mesmo cenário requer a realização de steps de maneiras distintas em diferentes sistemas operacionais (Android, iOS, Windows Phone, entre outros).

Desvantagens:
– Perda de legibilidade – os subpassos de um passo, quando em grande quantidade e implementados dentro das classes de telas (3ª camada), ficam difíceis de ler;
– Ofuscabilidade – as partes interessadas no projeto não terão conhecimento das ações do usuário em detalhes, a não ser que eles leiam as implementações das Screens, o que não é desejado.

Conclusão

Não existe a maneira certa de implementar os seus testes. Tanto priorizar os detalhes nas camadas de alto nível (primeira e segunda) quanto priorizar os detalhes nas camadas de baixo nível (segunda e terceira) tem vantagens e desvantagens.

No último projeto em que trabalhei aqui na Concrete, eu achei melhor seguir o primeiro padrão. Por outro lado, a Xamarin, com toda a sua experiência, sugere utilizar o segundo padrão. Eu pessoalmente me sentiria perdido tendo que agrupar um monte de passos em um único método de uma classe de Screen. Se eu tivesse algum passo repetido para outro cenário, teria praticamente que reescrever tudo!

Acredito que o segundo padrão seja mais útil quando os passos são realizados de maneira distinta entre as diferentes plataformas mobile. Ainda assim, nós aqui da Concrete concebemos aquele padrão de camadas já citadas no começo deste post:

image02

Pois, assim, podemos utilizar o primeiro padrão, caso a preferência seja deixar as diferenças entre as plataformas nas camadas de Feature ou de Steps.

E aí, já trabalhou com testes automatizados antes? Qual abordagem você se identificou mais e por quê? Mande seus comentários!

Fontes de inspiração:

Até a próxima!