DEV Community

Cover image for Testes de UI resilientes a mudanças
Tarcisio C. Uchoa
Tarcisio C. Uchoa

Posted on

1

Testes de UI resilientes a mudanças

Ref: Kent C. Dodds: Making your UI tests resilient to change
Ref: Autify Blog: Why you shouldn’t use ids in E2E testing
Ref: Cypress: Selecting Elements

Evitar buscar elementos pela tag genérica ou por classes, que estão sujeitas a alterações.

O ideal é fazer o teste o mais próximo possível de como o usuário faria.

O usuário não vai abrir o Dev Tools e procurar o elemento com o ID ou nome username-field, ele vai procurar o campo de usuário.

O cenário de teste mais próximo do que o usuário faria é:

  1. Digitar o nome de usuário no campo com a label "Nome de usuário"
  2. Digitar a senha no campo com a label "Senha"
  3. Clicar no botão que tem o texto "Entrar"

Mas nem sempre é possível seguir os passos do usuário dessa forma, sem falar que se basear no texto também está sujeito a alterações (o campo "nome de usuário" pode mudar para "Usuário").

Buscar pelo ID ou class é interessante, mas se um sistema que utiliza Bootstrap (.btn-primary) passa a utilizar Styled Components, ou Material UI, ou Tailwind... é preciso refazer todos os testes para adaptar às novas classes.

Se reutilizarmos nomes de classes por conta de estilo, ou alterarmos nomes de casses ou até mesmo a estrutura de componentes (uma <div> ou <span> a mais ou a menos), a cada alteração, o teste precisa ser reescrito, indicando um teste fraco e falho.

O problema principal é que a relação entre o código e o teste está muito implícita. É necessário superar essa dificuldade tornando a relação mais explícita.

Então a forma ideal é criar um data-attribute como data-test para identificar os elementos que devem ser testados, garantindo que o data-attribute não será utilizado para nenhum outro propósito.

Adicionando "metadata" aos elementos que serão explicitamente testados. Tornando a relação entre o código e o teste mais explícita e o desenvolvedor pode alterar ids, classes e estruturas sem dificuldade, desde que mantendo a indicação do teste explícita com o data-attribute .

<button
    type="submit"
    name="submit"
    id="form1234-submt"
    class="btn btn-primary"
    data-test="login-btn-submit"> ✅
    Enviar
</button>
Enter fullscreen mode Exit fullscreen mode

Mais código para escrever?

Embora seja a prática ideal, os desenvolvedores têm que se preocupar agora, além dos Ids , também com os data-test para manter a compatibilidade entre o código e o teste, evitando repeti-los ou removê-los ou alterá-los em refatorações de código ou criação de novos.

Uma possibilidade para diminuir riscos e ainda manter a referência é identificar componentes maiores com o data-attribute , em vez de identificar todos os componentes individualmente.

Talvez escrever:

<div class="modal" tabindex="-1" role="dialog" 👉🏻data-test="modal"👈🏻>
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">        
        <h5 class="modal-title">Confirm</h5>
          <button
            type="button"
            class="close"
            data-dismiss="modal"
            aria-label="Close">
          <span aria-hidden="true">&times;</span>        
        </button>      
      </div>      
      <div class="modal-body">
        <p>Really submit?</p>     
      </div>
      <div class="modal-footer">        
        <button
          type="button"
          class="btn btn-primary">
          Submit
        </button>
        <button
          type="button"
          class="btn btn-secondary"
          data-dismiss="modal">
            Cancel
        </button>
      </div>   
    </div> 
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Em vez de :

<div class="modal" tabindex="-1" role="dialog" 👉🏻data-test="modal"👈🏻>
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">        
                <h5 class="modal-title" 👉🏻data-test="modal-title"👈🏻>Confirm</h5>
                <button
                    type="button"
                    class="close"
                    data-dismiss="modal"
                    aria-label="Close"
                   👉🏻data-test="modal-close"👈🏻>
                    <span aria-hidden="true">&times;</span>        
                </button>      
            </div>      
            <div class="modal-body" 👉🏻data-test="modal-body"👈🏻>
                <p>Really submit?</p>     
            </div>
            <div class="modal-footer">        
                 <button
                     type="button"
                     class="btn btn-primary"
                    👉🏻data-test="modal-btn-submit"👈🏻>
                     Submit</button>        
                 <button
                     type="button"
                     class="btn btn-secondary"
                     data-dismiss="modal"
                    👉🏻data-test="modal-btn-cancel"👈🏻>Cancel</button>
            </div>   
        </div> 
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Assim é possível se basear no componente maior para localizar os itens internos:

this.page.locator('data-test=modal >> button:has-text("Submit")')
Enter fullscreen mode Exit fullscreen mode

O código acima busca somente o botão com o texto "Submit" que está dentro do elemento com o data-test=modal , ignorando outros botões, possivelmente com o mesmo texto.

Dependendo do formato e tamanho do projeto, isso pode ser útil.

Suja o código?

Se houver alguma preocupação com o fato de colocar em produção data-attributes que indiquem os testes (não é um grande problema, mas...), é possível remover esses atributos de produção com plugins como o babel-plugin-react-remove-properties.

Boas práticas do Cypress

Utilização de atributos data-* provém melhor contexto aos seletores, isolando das alterações de CSS e JavaScript.

  • Evitar localizar elementos por tags , classes e Ids , altamente sujeitos a mudanças.
  • É interessante localizar objetos pelo texto, se o conteúdo do texto fizer parte do cenário de teste, evitar se for um objeto que o texto pode ser alterado e não faça sentido para o teste.
  • Utilizar data-attributes facilita a identificação dos elementos exclusiva para testes, isolando de alterações de HTML, JS e CSS.

Quando utilizar a localização por texto?

await page.locator('text=Log in').click();
Enter fullscreen mode Exit fullscreen mode

Se o conteúdo (texto) do elemento alterar, você quer que o teste falhe?

  • Se SIM: localizar o elemento por texto
  • Se NÃO: localizar o elemento por data-attribute

Possível conclusão

  • O que deve ser imutável em uma aplicação não são suas casses ou Ids, mas o comportamento.
  • Quando o comportamento é alterado, o teste E2E deve detectar isso.
  • Testes E2E não devem travar o desenvolvimento além do comportamento.

Considerando isso, semelhante ao indicado nas boas práticas do Cypress, é interessante utilizar a localização por texto (quando necessário) e um pouco mais de data-attribute.

  • Localizar elementos por texto, quando necessário
  • Usar data-attributes, preferencialmente para identificar componentes UI e não todos os elementos individualmente.

Vantagens:

  • Não emprega Ids e classes internas, garantindo a manutenibilidade do código.
  • Comparado ao uso massivo de data-attributes, diminui o custo de desenvolvimento e manutenção de código.

Série de artigos sobre E2E:

  1. Teste E2E (end-to-end)
  2. Melhores práticas para criação e automação de testes E2E
  3. Testes de UI resilientes a mudanças
  4. Playwright: dicas e boas práticas
  5. Organização de testes automatizados

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay