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

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please show some love ❤️ or share a kind word in the comments if you found this useful!

Got it!