Mais um post da série qualidade de código em teste de software

Se você está chegando neste post agora e ainda não leu os conteúdos anteriores, recomendo começar por eles. Seguem os links:

Agora se você já leu o primeiro e segundo post da série, vamos falar sobre a terceira guideline do Better Code HubEscreva código uma só vez.

Segundo o BCH:

  • Quando código é copiado, bugs precisam ser corrigidos em vários lugares. Isso é ineficiente e sujeito ao erro.
  • Evite duplicação nunca copiando e colando blocos de código
  • Reduza duplicações extraindo código compartilhado, para uma nova classe, ou uma super-classe.

Quando código é copiado, bugs precisam ser corrigidos em vários lugares. Isso é ineficiente e sujeito ao erro

Trazendo isso para um exemplo de testes automatizados de interface gráfica para uma aplicação web escritos com o framework Cypress, veja como tal duplicação pode ocorrer.

No exemplo abaixo, temos testes para uma aplicação exemplo, a qual possui as funcionalidades de encurtar ou expandir uma URL digitada em um campo.

Ambos testes, para encurtar e expandir, repetem o mesmo passo de navegação à URL da aplicação, repetem o mesmo passo de digitar em um campo de input de texto, e repetem a verificação sobre um mesmo elemento.

describe("Sample app", () => {
  context("Shorten/Expand", () => {
    it("shortens a URL", () => {
      cy.visit("https://goo.gl/ZYQsTL");

      cy.get("input[type='text']").type("https://abcdefghijklmnopqrstuvwxyz.æåø");
      cy.contains("Shorten").click();

      cy.get("#responseField a").should("contain", "https://foo.bar");
    });

    it("expands a URL", () => {
      cy.visit("https://goo.gl/ZYQsTL");

      cy.get("input[type='text']").type("https://abc.de");
      cy.contains("Expand").click();

      cy.get("#responseField a").should("contain", "https://foo.bar.baz.bah.boo");
    });
  });
});

Agora imagine que a URL da aplicação mudou. Ambos os testes precisariam ser atualizados visto que tal informação está duplicada.

Isso parece não ser um grande problema agora, visto que só existem dois testes, mas imagine se fossem 50, ou 100 testes.

Imaginemos também que o ID do elemento que exibe o resultado da URL encurtada ou expandida mudou. Mais uma vez teríamos que atualizar ambos os testes.

Vejamos agora uma solução sem duplicação de código.

describe("Sample app", () => {
  context("Shorten/Expand", () => {
    beforeEach(() => cy.visit("https://goo.gl/ZYQsTL"));

    it("shortens a URL", () => {
      shortenUrl("https://abcdefghijklmnopqrstuvwxyz.æåø");

      verifyResponseFieldLinkContainUrl("https://foo.bar");
    });

    it("expands a URL", () => {
      expandUrl("https://abc.de");

      verifyResponseFieldLinkContainUrl("https://foo.bar.baz.bah.boo");
    });

    function shortenUrl(url) {
      typeUrl(url);
      cy.contains("Shorten").click();
    }

    function expandUrl(url) {
      typeUrl(url);
      cy.contains("Expand").click();
    }

    function typeUrl(url) {
      cy.get("input[type='text']").type(url);
    }

    function verifyResponseFieldLinkContainUrl(url) {
      cy.get("#responseField a").should("contain", url);
    }
  });
});

Perceba que o tamanho do arquivo de teste ficou maior, mas os testes propriamente ditos menores. Além disso, algumas funções foram criadas, as quais são utilizadas nos testes, e as quais podem no futuro ser utilizadas por outros testes também. O nome disso é reaproveitamento de código.

Em uma próxima iteração, tais funções poderiam ser movidas para o arquivo commands.js no diretório support/ do projeto de testes padrão criado pelo Cypress, e o código ficaria ainda mais limpo. Veja:

describe("Sample app", () => {
  context("Shorten/Expand", () => {
    beforeEach(() => cy.visit("https://goo.gl/ZYQsTL"));

    it("shortens a URL", () => {
      cy.shortenUrl("https://abcdefghijklmnopqrstuvwxyz.æåø");

      cy.verifyResponseFieldLinkContainUrl("https://foo.bar");
    });

    it("expands a URL", () => {
      cy.expandUrl("https://abc.de");

      cy.verifyResponseFieldLinkContainUrl("https://foo.bar.baz.bah.boo");
    });
  });
});

Neste terceiro exemplo tamanho do arquivo é menor, mais fácil de ler e entender.

Os exemplos acima podem ser encontrados a partir deste projeto exemplo em minha conta no GitLab.

Outro exemplo que demonstra como evitar duplicação de código pode ser visto aqui, onde em vez de criar 4 testes que fazem a mesma coisa, criei um único teste que itera em submenus e realiza a verificação baseado nos valores de um array.

Evite duplicação nunca copiando e colando blocos de código

A mensagem aqui é: se para sua próxima implementação você pensar em copiar um trecho de código e então colá-lo, pense em como evitar tal duplicação. Em muitos casos a simples criação de uma função genérica pode ser a resolução de seu problema, e além de você evitar a duplicação, no futuro você poderá utilizar tal função novamente.

Além disso, caso a lógica da função mudar, você só precisará se preocuprar em alterar o código em um lugar e todos os outros lugares que utilizam tal função continuarão funcionando.

Outra forma de evitar duplicação de código em testes automatizados é a atulização de funções como beforeEachbeforeAll, as quais executam callbacks antes de cada teste ou antes de todos os testes, respectivamente. Nos exemplos 2 e 3 da sub-sessão anterior utilizei a função beforeEach para acessar a URL da aplicação em teste antes de cada caso de teste, sem a necessidade de duplicar tal informação.

Reduza duplicações extraindo código compartilhado, para uma nova classe, ou uma super-classe

Este ponto, quando trazido para o mundo de testes automatizados de interface gráfica, pode ser utilizado quando temos uma arquitetura de testes com Page Objects e Componentes, por exemplo, onde podemos possuir alguns componentes comuns, os quais podem ser definidos como super classes, e componentes mais específicos podem então herdar os elementos destes componentes comuns.

No momento não tenho um exemplo em um projeto público para demonstrar isso, mas sugiro dar uma lida sobre herança em orientação à objetos, e no futuro irei atualizar este post com um exemplo real.


O próximo post da série será: Mantenha as interfáces da unidade pequenas.

Até a próxima e bons testes! 👋

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s