Os testes automatizados da interface do usuário são um tópico que até os desenvolvedores experientes desconfiam. Além disso, a tecnologia desses testes não é algo extraordinário e, no caso dos testes de interface do usuário codificados do Visual Studio, é uma extensão do sistema de testes de unidade interno do Visual Studio Team Test. Neste artigo, quero discutir o tópico dos testes de interface do usuário em geral, bem como nossa experiência pessoal de usar os Testes de UI codificados do Visual Studio ao trabalhar no analisador estático PVS-Studio.
O básico
Primeiro, vamos tentar descobrir por que os testes de interface do usuário não são tão populares entre os desenvolvedores quanto, por exemplo, os testes de unidade clássicos.
Existem muitas “pirâmides de teste” na rede que mostram a distribuição ideal ideal do número de testes nas camadas de aplicativos. Todas as pirâmides são semelhantes e contêm uma idéia geral: o maior número possível de testes deve estar o mais próximo possível do código. E vice-versa. Vou dar um exemplo de uma dessas pirâmides, que contém recomendações adicionais sobre a proporção do número de testes em porcentagem.

Na base da pirâmide estão os testes de unidade. De fato, esses testes são mais fáceis de fazer no estágio de desenvolvimento e serão executados muito rapidamente. No topo da pirâmide, por outro lado, há testes de interface automatizados. Esses testes não devem ser muitos, pois a complexidade de sua criação, bem como o tempo de execução, são bastante grandes. Além disso, não está claro a quem confiar a criação de testes de interface do usuário. Na verdade, estamos falando em emular ações do usuário. Tudo isso está muito longe do código do aplicativo, então os desenvolvedores relutam em fazer esse tipo de trabalho. E para fazer testes automatizados de alta qualidade de interfaces sem a participação (ou com participação mínima) dos programadores, é necessário o uso de ferramentas pagas. A combinação de todos esses fatores geralmente leva ao fato de que os testes de interface do usuário não funcionam, limitando-se a testes manuais únicos de novas funcionalidades. Além disso, os testes de interface são extremamente caros, não apenas no estágio de desenvolvimento, mas também durante o ciclo de vida do aplicativo. Mesmo uma pequena alteração na interface do usuário pode levar a erros na execução de muitos testes e à necessidade de modificá-los.
Observo que, atualmente, nosso sistema de teste geralmente cumpre as recomendações. O número de testes automatizados da GUI (45) é aproximadamente um décimo do número total de testes do PVS-Studio. Ao mesmo tempo, o número de testes de unidade não é tão grande, mas eles são complementados por vários outros sistemas de teste:
- Testes de desempenho dos analisadores (C / C ++ / C # / Java), durante os quais eles checam um grande conjunto de projetos de teste em diferentes sistemas operacionais (Windows, Linux, macOS) e comparam os logs de novos avisos com os de referência;
- Testes de recursos específicos (rastreando o lançamento de compiladores, etc.);
- Testes externos de aplicativos de linha de comando;
- Testes de montagem, instalação e implantação corretas;
- Testes de documentação.
No estágio inicial de seu desenvolvimento, o analisador PVS-Studio era um aplicativo para encontrar erros ao transportar o código C / C ++ para uma plataforma de 64 bits. Sim, e ele foi chamado naquela época de uma maneira diferente, "Viva64". A história do produto pode ser encontrada no artigo "
Como o projeto PVS-Studio começou há 10 anos ". Após a integração ao Visual Studio 2005, o analisador adquiriu uma interface gráfica do usuário, essencialmente a interface do próprio IDE do Visual Studio, na qual, após a instalação do plug-in, um menu adicional apareceu para acessar a funcionalidade do analisador. O cardápio era de dois ou três itens, então não havia nada para testar lá. E o Visual Studio naquela época não continha nenhuma ferramenta interna para testar GUIs.
Testes e alternativas de interface do usuário codificada do Visual Studio
Tudo mudou com o lançamento do Visual Studio 2010, que introduziu um sistema integrado para a criação de testes de interface do usuário: Visual Studio Coded UI Tests (CUIT). Com base no sistema de teste de unidade do Visual Studio Team Test, a CUIT usou inicialmente a tecnologia Microsoft Active Accessibility (MSAA) para acessar os elementos visuais da interface. No futuro, a tecnologia foi aprimorada e atualmente é um modelo desenvolvido de automação da interface do usuário para testar o código UIA (UI Automation). Ele permite que o sistema de teste acesse campos abertos (nome do objeto, nome da classe interna do objeto, estado atual do objeto, seu lugar na estrutura hierárquica da interface etc.) dos elementos da interface do usuário COM e .NET, e o sistema permite emular os efeitos desses elementos através de dispositivos de entrada padrão (mouse, teclado). Imediatamente, são suportados modos de gravação de ações do usuário ao interagir com uma interface (semelhante às macros do Visual Studio), automação da criação de um "mapa de interface" (propriedades de controles, parâmetros de pesquisa e acesso a eles), juntamente com a geração automática de código de controle. No futuro, todas as informações acumuladas são fáceis de modificar e manter atualizadas, além de personalizar as sequências de teste conforme desejado, além de possuir habilidades mínimas de programação.
Além disso, como eu disse anteriormente, agora, ao criar testes de interface do usuário inteligentes e complexos, você pode prescindir de nenhuma habilidade de programação, desde que use ferramentas pagas especializadas. Bem, se não houver desejo ou capacidade de usar ambientes de teste proprietários, há muitos produtos e estruturas gratuitos. O sistema de testes de interface do usuário codificada do Visual Studio é uma solução intermediária que permite não apenas automatizar o processo de criação de testes de interface do usuário, tanto quanto possível. Com sua ajuda, é fácil criar seqüências de teste arbitrárias nas linguagens de programação C # ou VB.
Tudo isso pode reduzir significativamente o custo de criação e manutenção da relevância dos testes da GUI. A estrutura usada é simples de entender e, em geral, pode ser representada na forma de um diagrama.
Entre os principais elementos, os chamados "adaptadores de interface" devem ser observados, que estão no nível mais baixo de abstração. Essa camada interage com os elementos finitos da interface do usuário e seus recursos podem ser expandidos usando adaptadores adicionais. Acima, há uma camada que oculta as tecnologias de acesso à GUI do restante do código, incluindo interfaces de acesso ao programa e o código real do aplicativo de teste, que inclui todos os elementos necessários para a automação de teste. A tecnologia é extensível, cada nível pode ser complementado com os elementos necessários dentro da estrutura da estrutura.
Os principais recursos do CUIT da Microsoft incluem:
- Teste funcional de interfaces de usuário. Aplicativos clássicos baseados no Windows, aplicativos da web e serviços, WPF são suportados
- Geração de código (inclusive automática) em VB / C #
- Capacidade de integrar no processo de montagem
- Lançamentos locais ou remotos, coleta de relatórios
- Disponibilidade de gravação e reprodução de sequências de teste
- Boa extensibilidade
Apesar de várias dificuldades associadas ao CUIT, o uso dessa tecnologia de teste é preferido por vários motivos:
- Interação eficaz de desenvolvedores e testadores em uma única ferramenta e linguagem de programação
- Recursos adicionais de trabalho com a “placa de interface”, permitindo a identificação de controles “on the fly”, sincronização de elementos e conclusão de sequências de teste
- Ajuste fino do mecanismo de reprodução, que permite, juntamente com as configurações básicas, como um atraso entre operações, um tempo limite de pesquisa de elemento, etc., usar mecanismos especializados no código. Por exemplo, bloqueando o segmento atual até que o controle seja ativado (visualizado) usando os métodos WaitForControlExist ou WaitForReady com a enumeração WaitForReadyLevel , etc.
- Capacidade de programar testes de complexidade ilimitada
Não vou aprofundar os aspectos teóricos da tecnologia de testes de interface do usuário codificada do Visual Studio, todos eles são detalhados na documentação relevante. Lá, você encontra instruções detalhadas para criar o teste de interface do usuário mais simples com base nesse sistema. E sim, o sistema não é gratuito, você precisará do Visual Studio Enterprise para trabalhar com ele.
A tecnologia descrita não é a única no mercado. Existem muitas outras soluções. Todos os sistemas alternativos de teste de interface do usuário podem ser divididos em pagos e gratuitos. Além disso, a escolha de um sistema específico nem sempre dependerá do seu preço. Por exemplo, a capacidade de criar testes sem a necessidade de programação pode servir como um fator importante, mas, ao mesmo tempo, os testes podem não ser suficientemente flexíveis. Também é importante oferecer suporte ao ambiente de teste necessário - sistemas operacionais e aplicativos. Finalmente, os recursos puramente específicos do aplicativo e sua interface podem influenciar a escolha. Aqui estão alguns sistemas e tecnologias populares para testar GUIs.
Pago :
TestComplete (SmartBear),
Teste Funcional Unificado (Micro Focus),
Squish (froglogic),
Ferramentas de Teste Automatizadas (Ranorex),
Funcional de Berinjela (berinjela), etc.
Grátis :
AutoIt (windows),
Selenium (web),
Katalon Studio (web, mobile),
Sahi (web),
Robot Framework (web),
LDTP (Linux Desktop Testing Project), frameworks de código aberto:
TestStack.White +
UIAutomationVerify ,. Biblioteca de automação NET do Windows , etc.
Obviamente, esta lista não está completa. No entanto, é óbvio que os sistemas livres geralmente se concentram em um sistema operacional específico ou em uma tecnologia de teste. A regra geral é que, entre os sistemas pagos, você encontrará muito mais rapidamente algo adequado especificamente para suas necessidades, o desenvolvimento e a manutenção de testes serão mais fáceis e a lista de ambientes de teste suportados é exaustiva.
No nosso caso, não havia problema de escolha: com o lançamento do Visual Studio 2010 com a adição de Testes de IU codificados, tornou-se possível adicionar facilmente um conjunto de testes funcionais ao nosso ambiente de teste para testar a interface do usuário do plug-in PVS-Studio para Visual Studio.
Testes de UI do PVS-Studio
Portanto, os testes de GUI em nossa empresa são utilizados há mais de 6 anos. O conjunto inicial de testes de interface do usuário para o Visual Studio 2010 foi baseado na única tecnologia MSAA (Microsoft Active Accessibility) disponível naquele momento. Com o lançamento do Visual Studio 2012, a tecnologia MSAA se desenvolveu significativamente e agora é chamada de UIA (UI Automation). Foi decidido continuar usando o UIA e deixar os testes baseados no MSAA para testar o plug-in do Visual Studio 2010 (oferecemos suporte e teste de plug-ins para todas as versões do Visual Studio, começando com o Visual Studio 2010).
Como resultado, criamos dois "ramos" de testes de interface do usuário. Além disso, no projeto de teste, esses dois ramos usavam um mapa de interface comum e código compartilhado. No código, parecia algo assim (método para redefinir as configurações do Visual Studio para o padrão antes de executar o teste):
public void ResetVSSettings(TestingMode mode) { .... #region MSAA Mode if (mode == TestingMode.MSAA) { .... return; } #endregion
Ao fazer alterações na interface do plug-in, foi necessário fazer alterações nas duas ramificações dos testes da interface do usuário e, ao adicionar novas funcionalidades, foi necessário duplicar o elemento da interface no mapa: ou seja, criar dois elementos diferentes para cada uma das tecnologias MSAA e UIA. Tudo isso exigiu muito esforço não apenas para criar ou modificar testes, mas também para manter o ambiente de teste em um estado estável. Vou me debruçar sobre esse aspecto com mais detalhes.
De acordo com minhas observações, a estabilidade do ambiente de teste ao testar a GUI é um problema significativo. Isso se deve principalmente à forte dependência de tais testes em muitos fatores externos. De fato, as ações do usuário são emuladas: pressionando teclas, movendo o cursor do mouse, cliques do mouse etc. Há muitas coisas que podem "dar errado". Por exemplo, se durante o teste alguém interage com um teclado conectado ao servidor de teste. Além disso, inesperadamente, a resolução do monitor pode não ser suficiente para exibir qualquer controle e não será encontrada pelo ambiente de teste.
Aguardando:
Realidade:
Um elemento do mapa da interface ajustado de forma descuidada (e não encontrada mais tarde) é praticamente o líder do comportamento do problema. Por exemplo, ao criar um novo controle, o assistente de mapa da interface Testes de IU Codificados do Visual Studio usa os critérios de pesquisa padrão Igual a ele. Ou seja, é necessária a correspondência exata dos nomes das propriedades com os valores especificados. Isso geralmente funciona, mas às vezes a estabilidade da execução do teste pode ser significativamente aprimorada usando os critérios de pesquisa menos rigorosos "Contém" em vez de "Igual a". Este é apenas um exemplo de um “ajuste” que você pode encontrar ao trabalhar nos testes de interface do usuário.
Por fim, alguns de seus testes podem consistir em executar uma ação e aguardar mais um resultado, que, por exemplo, está associado à exibição de uma janela. Nesse caso, para o problema de procurar um elemento, serão adicionadas perguntas sobre como definir o atraso da reprodução até que a janela apareça e sincronizar o trabalho. Alguns desses problemas podem ser resolvidos por métodos de estrutura padrão (
WaitForControlExist , etc.), enquanto para outros será necessário inventar algoritmos engenhosos.
Enfrentamos um problema semelhante ao trabalhar em um dos testes do nosso plugin. Neste teste, um ambiente vazio do Visual Studio é aberto primeiro e, em seguida, uma certa solução de teste é carregada lá, que é totalmente testada usando o PVS-Studio (menu “PVS-Studio” -> “Verificar” -> “Solução”). O problema foi determinar quando a verificação seria concluída. Dependendo de várias condições, a verificação nem sempre pode levar o mesmo tempo, portanto, tempos limite simples não funcionam aqui. Além disso, você não pode usar os mecanismos padrão para suspender o fluxo de teste e aguardar a aparência (ou ocultação) de qualquer controle, pois não há nada a que anexar. Durante a verificação, uma janela com o status do trabalho é exibida, mas essa janela pode estar oculta e a verificação continuará. I.e. esta janela não pode ser guiada (além disso, possui a configuração "Não fechar após a análise ser concluída"). E eu queria tornar o algoritmo mais geral, para usá-lo em vários testes relacionados à verificação de projetos e à espera pela conclusão desse processo. Uma solução foi encontrada. Após o início do teste e até sua conclusão, o próprio item de menu “PVS-Studio” -> “Check” -> “Solution” fica inativo. Nós apenas tivemos que verificar a propriedade “Enabled” deste item de menu em um determinado intervalo de tempo (através do objeto de mapa da interface) e, se for constatado que o elemento se tornou ativo, considere o processo de verificação de decisão completo.
Portanto, no caso de testes de interface do usuário, não basta criar testes. É necessário um ajuste sutil e meticuloso em cada caso. É necessário entender e sincronizar toda a sequência de ações executadas. Por exemplo, o item do menu de contexto não será encontrado até que este menu seja exibido na tela, etc. Também é necessária uma preparação cuidadosa do ambiente de teste. Nesse caso, você pode contar com a operação estável dos testes e com os resultados adequados.
Deixe-me lembrá-lo de que o sistema de testes de interface do usuário em nossa empresa está em desenvolvimento desde 2010. Durante esse período, várias dúzias de sequências de teste foram criadas e muitos códigos auxiliares foram escritos. Testes de aplicativos independentes foram adicionados aos testes de plug-in ao longo do tempo. Nesse ponto, a antiga ramificação de testes de plug-ins do Visual Studio 2010 havia perdido sua relevância e foi abandonada, mas era simplesmente impossível cortar esse código "morto" do projeto. Primeiramente, como mostrei anteriormente, o código foi profundamente integrado aos métodos de teste. Em segundo lugar, mais da metade dos elementos da placa de interface existente pertencia à antiga tecnologia MSAA, mas foram reutilizados (em vez de duplicados) em muitos novos testes junto com os elementos UIA (isso é possível devido à continuidade da tecnologia). Ao mesmo tempo, a massa do código gerado automaticamente e o conteúdo dos métodos de teste foram vinculados aos elementos "antigos".
No outono de 2017, havia uma necessidade de melhorar o sistema de testes de interface do usuário. Em geral, os testes funcionaram bem, mas de tempos em tempos alguns testes "travavam" por motivos desconhecidos. Mais precisamente, o motivo geralmente era encontrar um controle. Em cada caso, eu tive que percorrer a árvore do mapa de interface para um elemento específico e verificar seus critérios de pesquisa e outras configurações. Às vezes, uma redefinição do software dessas configurações ajudava antes de executar o teste. Dado o mapa de interface que cresceu (e em muitos aspectos, de várias maneiras) pela placa de interface, bem como a presença de código "morto", esse processo exigiu um esforço considerável.
Por algum tempo, a tarefa "estava esperando por seu herói", até que finalmente chegou a mim.
Não vou aborrecê-lo com uma descrição das nuances. Só posso dizer que o trabalho não foi difícil, mas exigiu considerável perseverança e atenção. Tudo sobre tudo me levou cerca de duas semanas. Passei metade desse tempo refatorando as placas de código e interface. No tempo restante, ele se envolveu na estabilização da execução do teste, que basicamente se resumia a um ajuste mais preciso dos critérios de busca por elementos visuais (edição do mapa da interface), além de alguma otimização do código.
Como resultado, conseguimos reduzir o tamanho do código dos métodos de teste em cerca de 30%, e o número de controles na árvore do mapa de interface foi reduzido pela metade. Mas o mais importante, os testes de interface do usuário começaram a mostrar um desempenho mais estável e requerem menos atenção. E a queda começou a ocorrer com mais frequência devido a razões para fazer alterações na funcionalidade do analisador ou ao detectar inconsistências (erros). Na verdade, para esses fins, precisamos de um sistema de testes de interface do usuário.
Assim, atualmente, o sistema de testes automáticos da interface PVS-Studio possui as seguintes características básicas:
- Teste de interface do usuário codificada do Visual Studio
- 45 cenários
- 4.095 linhas de código para métodos de teste
- 19.889 linhas de código gerado automaticamente (excluindo o tamanho do arquivo xml para armazenar as configurações do Mapa da UI)
- 1 hora 34 minutos de execução (valor médio de acordo com os resultados das últimas 30 partidas)
- Trabalhar em um servidor dedicado (executando o utilitário MSTest.exe)
- Monitoramento de desempenho e análise de relatório de desempenho (Jenkins)
Conclusão
Concluindo, desejo fornecer uma lista de critérios de sucesso para testes automáticos de GUI, que se baseiam em uma análise de nossa experiência com essa tecnologia (alguns critérios se aplicam a outras tecnologias de teste, por exemplo, testes de unidade).
Ferramentas adequadas . Escolha o ambiente para criar e executar o CUIT de acordo com os recursos do seu aplicativo, bem como o ambiente de teste. As soluções pagas nem sempre fazem sentido, mas geralmente ajudam a resolver um problema com muita eficiência.
Configuração de infraestrutura de alta qualidade . Não salve ao desenvolver uma placa de interface. Simplifique o trabalho da estrutura ao procurar elementos, descrevendo cuidadosamente todas as suas propriedades e definindo critérios de pesquisa inteligente. Preste atenção às possibilidades de outras modificações.
Minimização do trabalho manual . Sempre que possível, certifique-se de usar meios automáticos para gerar código e gravar sequências. Portanto, você acelerará significativamente o desenvolvimento e minimizará a probabilidade de erros (nem sempre é fácil encontrar o motivo do travamento do teste da interface do usuário, especialmente se um erro for cometido no código para trabalhar com a estrutura).
Testes inteligentes simples e independentes . Quanto mais simples seus testes, melhor. Tente fazer um teste separado para testar um controle específico ou uma situação simulada. Verifique também se os testes são independentes um do outro. A queda de um dos testes não deve afetar todo o processo.
Nomes amigáveis . Use prefixos nos nomes de testes semelhantes. Muitos ambientes permitem executar testes filtrando por nome. Use também o agrupamento de testes sempre que possível.
Tempo de execução isolado . Verifique se os testes são executados em um servidor dedicado com impacto externo mínimo. Desconecte todos os dispositivos de entrada do usuário externo, forneça a resolução de tela necessária para o seu aplicativo ou use um manequim de hardware que simule uma conexão de monitor de alta resolução. Verifique se, durante o teste, outros aplicativos que interagem, por exemplo, com a área de trabalho e exibem mensagens, não estão em execução. Também é necessário planejar a hora de início e considerar a duração máxima dos testes.
Análise de relatórios emitidos . Forneça um formulário simples e claro para relatar o progresso. Use sistemas de integração contínua para despachar testes, bem como obter e analisar rapidamente os resultados dos testes.

Se você deseja compartilhar este artigo com um público que fala inglês, use o link para a tradução: Sergey Khrenov.
Testes de UI codificados do Visual Studio: teoria e experiência do usuário da empresa