Como escrever testes de unidade de manutenção, e não quebradiços, para uma GUI?


16

Tentei escrever testes de unidade de interface do usuário para meus aplicativos de GUI e enfrento o problema de que, embora funcionem bem quando os escrevo inicialmente, eles se tornam quebradiços e quebram sempre que o design é alterado (ou seja, com frequência). Estou lutando para encontrar um conjunto de diretrizes que me levem a realizar testes de unidade sustentável para a GUI.

Por enquanto, uma coisa que eu descobri é que os testes dizendo "esse componente deve mostrar seus dados de entrada em algum lugar" são bons (e isso é muito fácil com o HTML). Testes que verificam um estado específico de uma parte específica do componente geralmente são frágeis. Testes como click-click-click-expect, que tentam seguir o comportamento do usuário e a lógica comercial subjacente (que é a parte mais importante) geralmente acabam quebradiços. Como escrevo bons testes?


Para ser mais preciso, eu gostaria de conhecer alguns padrões sobre o que eu poderia testar na minha interface do usuário, não exatamente como testá-lo. Convenções de nomenclatura e identificadores fixos são bons, mas não resolvem o problema principal, ou seja, as GUIs mudam muito. Eu gostaria de testar os comportamentos que provavelmente não mudarão. Como encontrar a coisa certa para testar?


1
@ MichaelDurrant: Você editou a pergunta como sendo sobre testes de interface do usuário em geral, enquanto eu originalmente perguntava sobre testes de unidade. Acho os testes de integração mais difíceis de manter e prefiro os testes de unidade sobre eles, sempre que possível.
— mik01aj

2
Eu acho que isso é parte do problema, no entanto, fundamentalmente, você não pode realmente testar nenhuma interface apenas por testes unitários, pois a razão de ser deles é a interface com alguma coisa. As GUIs não são diferentes nesse aspecto.
— jk.

m01, você pode mudar de volta. Penso nos testes de interface do usuário como sendo geralmente testes integrados. Os testes tendem a depender dos dados de sementes e acessórios presentes e da interface do usuário trabalhando com eles. Se você tiver testes de interface do usuário verdadeiros que não dependem de outros dados excelentes. No entanto, eu achei isso relativamente raro.
— Michael Durrant

2
relacionados, mas não uma duplicata vez que este é sobre testes de GUI: programmers.stackexchange.com/questions/109703/...
— k3b

Respostas:


3

Um problema comum com os testes da GUI ... A principal razão pela qual esses testes são considerados frágeis é porque eles não podem sobreviver a uma mudança na GUI que não é uma alteração nos requisitos . Você deve estruturar seu código de teste de forma que uma alteração na GUI seja isolada em um único local nos testes.

Como exemplo, considere um teste com a seguinte redação:

Quando o usuário digita '999' no campo número de telefone e clica no botão Salvar, o pop-up da mensagem de erro deve dizer 'número de telefone inválido'.

Aqui há bastante espaço para que esse teste seja interrompido quando você refaz a interface, mesmo que o requisito para a validação permaneça.

Agora, vamos colocar isso em um pequeno texto alternativo:

Quando o usuário digita '999' como um número de telefone e salva a página de perfil, ele deve mostrar um erro que diz 'número de telefone inválido'

O teste é o mesmo, os requisitos são os mesmos, mas esse tipo de teste sobreviverá a uma reforma na sua interface do usuário. Você precisará alterar o código, obviamente, mas o código será isolado. Mesmo que você tenha dez ou vinte testes para a sua página de perfil e mova sua lógica de validação que exibe mensagens de erro de alertas javascript para jquery-pop-ups, por exemplo, você só precisa alterar a parte do teste que verifica as mensagens de erro.


4

Esse é um problema comum. Eu prestaria atenção a:

  • Como você nomeia elementos

    Use css id ou classe para identificar elementos. Favor usar o ID do CSS quando o objeto for único. Considere a estrutura que você está usando, por exemplo, com o Ruby on Rails, o nameatributo é atribuído automaticamente e pode (não intuitivamente) ser melhor do que usar o id ou classe css

  • Como você identifica elementos.

    Evite identificadores posicionais como table/tr/td/tdem favor de formas comotd[id="main_vehicle" ou td[class='alternates']. Considere usar atributos de dados quando apropriado. Ainda melhor, tente evitar as tags de layout, como <td>as anteriores, para adicionar um span e usá-lo, por exemplo, <span id="main_vehicle">ou um seletor de curinga, como *[id="main_vehicle"]onde *agora pode ser um div, span, td, etc.

  • Usando atributos de dados específicos de teste que são usados ​​apenas para qa e teste.

  • Evite qualificação desnecessária para elementos. Você pode encontrar o seguinte:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    No entanto, isso exige que o campo de entrada permaneça em um formulário com um ID exato do veículo e em uma página com uma carroceria que possua uma classe de main e uma div com um ID de veículos que tenha um filho imediato de um formulário com um ID de veículo. Qualquer alteração em qualquer estrutura e o teste é interrompido. Nesse caso, você pode achar que

    input#primary_vehicle_name

    é suficiente para identificar exclusivamente o elemento.

  • Evite testes que se refiram ao texto visível. O texto na página que é mostrado ao usuário geralmente muda com o tempo, à medida que o site é mantido e atualizado; portanto, use identificadores como id css e classe css ou atributos de dados. Elementos como form, inpute selectusados ​​em formulários, também são boas partes dos elementos de identificação, geralmente em combinação com ID ou classe, por exemplo, li.vehicleou input#first-vehicle Você também pode adicionar seus próprios identificadores, por exemplo<div data-vehicle='dodge'> . Dessa forma, você pode evitar o uso de IDs ou classes de elementos, que provavelmente serão alterados por desenvolvedores e designers. Na verdade, descobri com o tempo que é melhor trabalhar apenas com desenvolvedores e designers e chegar a um acordo sobre nomes e escopos. É difícil.

  • Como os dados fixos são mantidos.

    Semelhante à identificação de elementos reais, tente evitar o seletor codificado em linha, identificando valores em favor dos objetos de página - pequenos pedaços de texto que são mantidos em variáveis ​​ou métodos e, portanto, podem ser reutilizados e também mantidos centralmente. Exemplos de variáveis ​​javascript seguindo este padrão para valores codificados:

    storedVars["eqv_auto_year"] = "2015";
    storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
    storedVars["eqv_auto_make_2"] = "HONDA";`  
    

    Mais informações sobre objetos de página no selenium wiki e selenium docs

  • Comunicação com desenvolvedores.

    Independentemente da abordagem técnica em termos de 'desenvolvedores fazem alterações e interrompem a automação do controle de qualidade', isso é um problema do fluxo de trabalho. Você precisa ter certeza de que: todos são da mesma equipe; o desenvolvedor executa os mesmos testes integrados; os padrões são acordados e seguidos pelos dois grupos; a definição de done inclui executar e possivelmente atualizar os testes da interface do usuário; desenvolvedores e testadores fazem par de planos de teste e participam da preparação do ticket (se estiver fazendo o Agile) e falam sobre o teste da interface do usuário como parte da preparação. Você deve garantir que qualquer abordagem e estratégia usada para nomear seja coordenada com os desenvolvedores de aplicativos. Se você não chegar à mesma página, entrará em conflito com a nomeação de objetos. Alguns exemplos de métodos de objeto de página que criei recentemente para um projeto ruby:

    def css_email_opt_in_true
      'auto_policy[email_opt_in][value=1]'
    end 
    
    def css_phone_opt_in
      '*[name="auto_policy[phone_opt_in]"]'
    end 
    
    def css_phone_opt_in_true
      'input[name=phone_opt_in][value=true]'
    end 
    
    def css_credit_rating
      'auto_policy[credit_rating]'
    end
    

    Aqui estão os mesmos objetos de página que as variáveis ​​javascript:

    storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
    storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
    storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
    storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
    

2
Essas são dicas úteis, e na verdade eu já sigo a maioria delas. Meu problema era que eu escrevia testes para algum botão e, em seguida, esse botão é removido enquanto a mesma ação é manipulada de outro lugar. Ou o botão permanece lá, mas o rótulo muda e o botão também executa alguma ação adicional.
— Mk01aj 19/10/2015

1
Ahh, isso é bom de saber. Sim para as coisas que eu iria incidir sobre o fluxo de trabalho e organizacional interação qa-desenvolvedor
— Michael Durrant

1
Também estou postando informações para os outros que encontrarem sua pergunta e talvez não possuam todas as peças que você possui ou talvez nem conheça.
— Michael Durrant

1
Expandi meu último ponto de bulllet com base nos seus comentários.
— Michael Durrant

1
E atualizei as partes sobre nomear e identificar elementos e sobre não usar texto visível.
— Michael Durrant

3

A razão pela qual as pessoas desenvolveram coisas como MVC, MVP e apresentador primeiro e padrões de design semelhantes foi separar a lógica de negócios da interface do usuário.

Obviamente, a parte da visualização só pode ser testada iniciando o programa e verificando o que mostra - em outras palavras, ela só pode ser testada em testes de aceitação.

Por outro lado, o teste da lógica de negócios pode ser feito em testes de unidade. E essa é a resposta para sua pergunta. Teste tudo no modelo e, se puder e quiser, também pode testar o código do controlador.


As GUIs mudam bastante

Isso só pode acontecer quando você muda os requisitos. Quando um requisito muda, não há como contorná-lo, exceto para modificar o código. Se você conseguir criar um bom design e arquitetura, a mudança não será propagada em muitos lugares.


2
Eu acho que é um bom argumento. Talvez eu esteja apenas fazendo o MVC errado :) Btw, acredito que a mudança de requisitos é inevitável quando você desenvolve uma plataforma da Web para muitos usuários. Você não sabe como seus usuários se comportarão até que eles comecem a usar a GUI.
— mik01aj

2

Os testes de interação da GUI não devem ser mais ou menos frágeis do que qualquer outro tipo de teste. Isso é; se o seu aplicativo estiver mudando de alguma forma, os testes precisam ser atualizados para refletir isso.

Como comparação:

Teste de unidade

Original : validateEmail()deve lançar uma InvalidDataexceção. Qual é coberto corretamente no seu teste de unidade.

Mudança : validateEmail()deve lançar uma InvalidEmailexceção. Agora seu teste está incorreto, você o atualiza e tudo fica verde novamente.

Teste da GUI

Original : a inserção de um email inválido resultará em uma caixa de erro pop-up contendo "Dados inválidos digitados". Detectado corretamente pelos seus testes.

Alteração : a inserção de um email inválido resultará em um erro embutido contendo "Email inválido digitado". Agora seu teste está incorreto, você o atualiza e tudo fica verde novamente.


Lembre-se de que você está testando entradas e saídas - algum comportamento bem definido. Independentemente de se tratar de um teste de GUI ou de unidade ou de integração, etc.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.