Em vez de 100 lançamentos de aplicativos - um autoteste ou como salvar um engenheiro de controle de qualidade 20 anos de vida

Olá pessoal, meu nome é Evgeny Demidenko. Nos últimos anos, desenvolvo um sistema automatizado de teste de jogos na Pixonic. Hoje eu queria compartilhar nossa experiência no desenvolvimento, suporte e uso de um sistema desse tipo no projeto War Robots.

Para começar, descobriremos o que estamos automatizando com este sistema.

Primeiro, são testes de regressão da interface do usuário, testes de core-gameplay e automação de benchmarks. Todos os três sistemas como um todo tornam possível reduzir a carga sobre o departamento de controle de qualidade antes dos lançamentos, ter mais confiança na refatoração em larga escala e profunda e manter constantemente uma avaliação geral do desempenho do aplicativo, bem como de suas partes individuais. Outro ponto que quero destacar é a automação da rotina, por exemplo - teste de qualquer hipótese.

imagem

Vou dar alguns números. Agora, mais de 600 testes de interface do usuário e cerca de 100 testes principais foram escritos para os robôs de guerra. Somente neste projeto, fizemos cerca de um milhão de lançamentos de nossos scripts de teste, cada um dos quais levou cerca de 80 segundos. Se verificássemos esses cenários manualmente, teríamos gasto pelo menos cinco minutos cada. Além disso, lançamos mais de 700 mil benchmarks.

Das plataformas, usamos Android e iOS - apenas 12 dispositivos no parque. Dois programadores estão envolvidos no desenvolvimento e suporte do sistema, e um engenheiro de controle de qualidade está escrevendo e analisando testes.






Quanto à pilha de software, usamos o NUnit em nosso banco de dados, mas não para testes de unidade, mas para testes de integração e sistema. Para jogabilidade central e testes de verificação de compilação, usamos a solução integrada do Unity - Unity Test Tools. Para escrever e analisar relatórios após esses testes, o Allure Test Report da Yandex é usado, assim como o TeamCity - como um sistema de integração contínua para a criação de aplicativos, implantação de servidores e execução de testes. Usamos o Repositório Nexus e o banco de dados PostgreSQL para armazenar nossos artefatos.




Como você cria, analisa e executa testes


Suponha que desejemos escrever um teste simples que, na janela de configurações do jogo, verifique o ícone para ativar e desativar o som.

Então, escrevemos um teste e o comprometemos com uma ramificação específica em nosso repositório de testes. Escolhemos os testes que queremos executar, escolhemos uma compilação para executar, ou talvez um commit específico, no qual a compilação será montada. Agora execute o teste, aguarde um pouco e obtenha o resultado.





Nesse caso, 575 testes foram lançados, dos quais 97% foram bem-sucedidos. Demoramos cerca de três horas para concluir todos os testes. Para comparação, os mesmos testes, se feitos manualmente, levariam pelo menos 50 horas de operação contínua.

Então, o que aconteceu com esses 3% dos testes que falharam?

Abrimos um teste específico e vemos uma mensagem de que ocorreu um erro ao corresponder às capturas de tela.



Em seguida, abrimos a captura de tela, que naquele momento estava no dispositivo, e vemos que as zonas que não correspondem ao original são marcadas com pixels vermelhos. Para comparação, nós damos a ele.





Naturalmente, depois disso, o engenheiro de controle de qualidade deve cometer um erro para que o comportamento da compilação não corresponda ao documento de design do jogo ou atualizar as capturas de tela originais, porque o documento de design do jogo mudou e agora esses elementos não estarão no jogo.


Isso parece legal. Por que tudo isso é necessário?


Há algum tempo, no projeto War Robots, precisávamos fazer uma pequena refatoração. Consistia em reescrever alguns códigos para disparar armas - em particular, metralhadoras.

Durante os testes, encontramos uma nuance interessante: a taxa de metralhadoras dependia diretamente do FPS. Esse erro seria irreal para detectar durante o teste manual: primeiro, devido aos recursos de cálculo de danos na rede do projeto e, segundo, devido ao fato de o aplicativo War Robots estar bastante otimizado e, naquele momento, ser executado em todos os dispositivos com aproximadamente o mesmo FPS - 30 quadros / s. Obviamente, houve pequenos desvios, mas não foram suficientes para notar um aumento no dano causado pelo disparo de armas durante o teste manual. Então nos perguntamos: quantos desses bugs ainda temos e quantos podem aparecer durante a refatoração?

Como não queríamos reduzir o número de testes, mas aumentá-los, já que tínhamos planejado grandes atualizações e um aumento no número de conteúdo, não queríamos crescer horizontalmente e aumentar o número de funcionários do departamento de controle de qualidade. Em vez disso, planejamos o crescimento vertical com uma redução na rotina dos funcionários atuais e facilitando a vida deles durante os testes de integração de novos conteúdos.




Quais ferramentas usamos


Quando começamos a automatizar os testes, chamamos a atenção para as Ferramentas de Teste de Integração do Unity, que estavam embutidas naquele momento no Unity. Escrevemos várias interfaces de usuário e testes principais, concluímos a refatoração que começamos anteriormente e ficamos satisfeitos com ela, porque a solução já funcionava, o que significa que nossas suposições estavam corretas e tivemos que seguir em frente. O único aspecto negativo desta solução, mas muito significativo para nós, foi que os testes não puderam ser executados em dispositivos móveis.

Assim, tivemos a ideia de usar a estrutura Appium. Este é um garfo de outra estrutura de teste bem conhecida - Selenium. Por sua vez, talvez seja a estrutura mais famosa para testar aplicativos da Web, cujo conceito principal é trabalhar com elementos da interface do usuário, obter suas coordenadas e organizar a entrada nesses elementos da interface do usuário. A Appium adotou esse conceito e, além dos drivers da Web existentes no Selenium, também adicionou drivers para iOS e Android: eles usam estruturas de teste nativas para cada uma dessas plataformas.

Como não há elementos de interface do usuário nativos no Unity, e há apenas um elemento de interface do usuário no qual a imagem é renderizada, tive que adicionar um acréscimo ao Appium UnityDriver, que permite trabalhar com a hierarquia de cenas, obter objetos de cenas e muito mais.

Naquele momento, um engenheiro de controle de qualidade já havia aparecido no projeto, as coisas começaram a fluir, o número de cenários de teste começou a crescer significativamente, o que gradualmente automatizamos. Começamos a lançá-los em dispositivos e, em geral, nosso trabalho já parecia da maneira que queríamos.

No futuro, além dos testes de interface do usuário, começaram a aparecer mais testes básicos e outras ferramentas baseadas em nosso sistema, como resultado do desempenho e qualidade do trabalho em vários dispositivos, suporte adicional para vários outros dispositivos, testes paralelos e também abandonamos o Appium em benefício de sua própria estrutura.



O único problema que permaneceu conosco - e ainda é - foi a hierarquia da interface do usuário. Como se uma hierarquia mudar em uma cena devido à refatoração da interface do usuário ou trabalhar na cena, isso precisa ser suportado nos testes.

Após as próximas inovações e revisões, a arquitetura de todo o sistema começou a ter a seguinte aparência.



Nós pegamos a versão do War Robots, pegamos nossos testes, que estão em um repositório separado, adicionamos alguns parâmetros para rodar lá que nos permitem configurar o início dos testes em cada caso e enviamos tudo para o agente TeamCity em um PC remoto. O agente TeamCity lança nossos testes, passa os parâmetros de construção e lançamento de "Robôs", após os quais os testes começam a funcionar e a "comunicar" independentemente com os dispositivos conectados ao agente do TeamCity por fio: coloque construções neles, execute-os, execute determinados scripts, exclua cria, reinicie o aplicativo e assim por diante.

Como os testes e o próprio aplicativo são executados em dispositivos fisicamente diferentes - em um telefone celular e Mac mini -, precisamos implementar a comunicação entre nossa estrutura, a War Robots API e a Unity API. Adicionamos um pequeno servidor UDP ao aplicativo, que recebe comandos da estrutura e se comunica com a API do aplicativo e o Unity por meio de manipuladores.



A principal tarefa de nossa estrutura é organizar o trabalho de testes: a correta preparação, conclusão e gerenciamento de dispositivos. Em particular, paralelismo para acelerar o trabalho, a escolha certa de dispositivos e capturas de tela, comunicação com a compilação. Após concluir os testes, nossa estrutura deve salvar todos os artefatos gerados e gerar um relatório.


Dicas para escolher dispositivos


Separadamente, quero prestar atenção à escolha dos dispositivos para teste.

Atenção considerável deve ser dada aos hubs. Se você deseja executar benchmarks em seus dispositivos - especialmente se forem dispositivos Android - eles ficarão sem energia. Os hubs devem fornecer a energia necessária para os dispositivos usados. Há outro recurso muito sutil: alguns hubs têm energia ativa, e essa energia é desligada após surtos de energia, após o que é ativada apenas pressionando-se fisicamente um botão. Temos esses hubs, e isso é muito inconveniente.

Se você deseja executar testes de interface de usuário de regressão e lógica de teste em dispositivos, não use dispositivos diferentes. Adote os mesmos dispositivos - melhor os mais produtivos que você puder pagar, pois assim você economizará tempo com os freios do dispositivo, a conveniência de trabalhar com eles e o comportamento do aplicativo em todos os dispositivos será o mesmo.

Uma questão separada é o uso de farms na nuvem. Ainda não os usamos, embora tenhamos pesquisado sobre eles: o que são, quanto custam e como executar nossos testes - mas até agora dispomos de nosso parque de dispositivos interno para atender nossos pedidos.


Relatórios de teste


Após a conclusão dos testes, geramos um relatório de fascínio, que inclui todos os artefatos criados durante o teste.

O principal "cavalo de batalha" para analisar o que aconteceu e identificar as causas do acidente durante o teste são os logs. Antes de tudo, nós os coletamos de nossa estrutura, que nos fala sobre o estado do script e o que aconteceu nesse script. Dividimos os logs no sistema (mais detalhado) e o log de controle de qualidade (mais compacto e conveniente para análise). Também coletamos logs do sistema de dispositivos (por exemplo, logcat) e logs de um aplicativo Unity.

Durante a queda dos testes, também tiramos uma captura de tela para entender o que estava acontecendo nos dispositivos no momento da queda, gravamos um vídeo para entender o que aconteceu antes da falha e tentamos coletar o máximo de informações sobre o status do dispositivo, como os pings de nossos servidores e ifconfig para entender se o dispositivo possui um IP. Você ficará surpreso, no entanto, se você iniciar o aplicativo manualmente 50 vezes, tudo ficará bem com ele, mas se você o executar 50 mil vezes no modo automático, descobrirá que a Internet no dispositivo pode ser perdida e não ficará clara durante o teste, se havia uma conexão antes e depois da queda.

Também coletamos uma lista de processos, energia da bateria, temperatura e geralmente tudo o que podemos alcançar.




O que são boas capturas de tela e vídeos


Há algum tempo, nosso engenheiro de controle de qualidade sugeriu, além de capturar capturas de tela no outono, em certos locais dos testes, para comparar essas capturas de tela com os modelos que estão em nosso repositório. Assim, ele propôs economizar tempo no número de execuções de teste e reduzir o tamanho da base de código. Ou seja, com um teste, poderíamos verificar a lógica e a parte visual. Do ponto de vista do conceito de teste de unidade, isso não é muito correto, porque em um teste não devemos testar várias hipóteses. Mas este é um passo deliberado: sabemos como analisar tudo isso corretamente, por isso nos aventuramos a adicionar funcionalidades semelhantes.

Antes de tudo, pensamos em adicionar bibliotecas para corresponder às capturas de tela, mas percebemos que o uso de imagens com resoluções diferentes não é muito confiável, por isso paramos em dispositivos com a mesma resolução e comparamos as imagens com um determinado limite pixel por pixel.



Um efeito muito interessante do uso da correspondência de capturas de tela é que, se for difícil automatizar algum processo, nós o automatizaremos o quanto for possível, e simplesmente olharemos as capturas de tela manualmente. Foi exatamente isso que fizemos com a localização de teste. Recebemos uma solicitação para testar a localização de nossos aplicativos. Por isso, começamos a examinar as bibliotecas que permitem o reconhecimento de texto - mas percebemos que isso era pouco confiável e, como resultado, escrevemos vários scripts que "percorrem" telas diferentes e causam pop-ups diferentes. ups e, neste momento, são criadas capturas de tela. Antes de iniciar esse script, alteramos o código do idioma no dispositivo, executamos o script, capturamos capturas de tela, alteramos o código do idioma novamente e executamos o script novamente. Assim, todos os testes são executados à noite,para que, de manhã, o engenheiro de controle de qualidade possa analisar 500 capturas de tela e analisar imediatamente se há problemas com a localização em algum lugar. Sim, as capturas de tela ainda precisam ser assistidas, mas isso é muito mais rápido do que passar manualmente por todas as telas do dispositivo.

Às vezes, capturas de tela e logs não são suficientes: algo estranho começa a acontecer nos dispositivos, mas como eles estão localizados remotamente, você não pode avaliar o que aconteceu lá. Além disso, às vezes não está claro o que aconteceu literalmente alguns momentos antes do teste cair. Portanto, adicionamos uma gravação de vídeo do dispositivo, que começa com o início do teste e é salva apenas no caso de uma queda. Com a ajuda de tais vídeos, é muito conveniente rastrear falhas e congelamentos de aplicativos.




O que mais o nosso sistema pode fazer?


Há algum tempo, do departamento de testes de controle de qualidade, recebemos uma solicitação para desenvolver uma ferramenta para coletar métricas durante os testes manuais.

Para que serve?

Isso é necessário para que os engenheiros de controle de qualidade, após um teste manual, possam analisar adicionalmente o comportamento do FPS e do consumo de memória no aplicativo, analisando simultaneamente capturas de tela e vídeos que refletem o que estava acontecendo neste dispositivo.

O sistema desenvolvido por nós funcionou da seguinte forma. O engenheiro de controle de qualidade lançou o War Robots no dispositivo, ativou a gravação da sessão do playbench - nosso análogo do gamebench - reproduziu o playtest e, em seguida, clicou em "encerrar a sessão do playbench", o relatório gerado foi salvo no repositório, após o que o engenheiro com os dados desse playtest poderia alcançar seu trabalho máquinas e veja o relatório: quais foram os rebaixamentos no FPS, qual consumo de memória, o que estava acontecendo no dispositivo.

Também automatizamos o lançamento de benchmarks no projeto War Robots, essencialmente apenas agrupando os benchmarks existentes em um lançamento automático. O resultado dos benchmarks é geralmente um dígito. No nosso caso, esse geralmente é o FPS médio por benchmark. Além do lançamento automático, decidimos adicionar outra sessão de playbench e, assim, recebemos não apenas uma figura específica, como o benchmark funcionava, mas também informações graças às quais podemos analisar o que aconteceu com o benchmark naquele momento.

Também devemos mencionar o teste de solicitação de recebimento. Desta vez, foi mais para ajudar a equipe de desenvolvimento do cliente, em vez de engenheiros de controle de qualidade. Executamos o chamado teste de verificação de compilação para cada solicitação de recebimento. Você pode executá-los nos dispositivos e no editor do Unity para acelerar o trabalho de verificação da lógica. Também executamos um conjunto de testes principais em ramificações separadas, onde ocorre uma espécie de reprojeto de alguns elementos ou refatoração de código.




E outros recursos úteis


No final, quero me debruçar sobre alguns casos interessantes que conhecemos nos últimos anos.

Um dos casos mais interessantes que apareceu recentemente conosco é o benchmark durante brigas com bots.

Para o novo projeto, o Pixonic Dino Squad desenvolveu um sistema no qual o engenheiro de controle de qualidade podia fazer um teste de brincadeira com bots, para não esperar por seus colegas, mas testar algumas hipóteses. Nosso engenheiro de controle de qualidade, por sua vez, pediu para adicionar a capacidade não apenas de brincar com bots, mas também para que eles possam brincar entre si. Assim, simplesmente lançamos o aplicativo e, neste momento, o bot começa a jogar com outros bots. Ao mesmo tempo, toda a interação é em rede, com servidores reais, ao invés de jogadores jogando no computador. Tudo isso é envolvido em benchmarks e uma sessão de playbench com gatilhos para o início da noite. Assim, à noite, iniciamos várias batalhas entre bots e bots; nesse momento, o FPS e o consumo de memória são gravados, capturas de tela são capturadas e vídeos são gravados. De manhã, o engenheiro de controle de qualidade chega e pode ver,quais testes foram realizados e o que aconteceu neles.

Também vale a pena conferir o vazamento de textura. Esse é um tipo de subanálise do uso de memória - mas aqui verificamos principalmente o uso, por exemplo, de texturas de garagem em batalha. Consequentemente, na batalha não deve haver atlas usados ​​na garagem e, quando saímos da batalha, as texturas usadas na batalha não devem permanecer na memória.

Um efeito colateral interessante do nosso sistema é que, quase desde o início de seu uso, rastreamos o tempo de carregamento do aplicativo. No caso da War Robots, esse tempo não é forte, mas está em constante crescimento, porque novos conteúdos estão sendo adicionados e a qualidade desse conteúdo está melhorando - mas podemos manter esse parâmetro sob controle e sempre estar ciente de seu tamanho.


Em vez de uma conclusão


No final, gostaria de chamar a atenção para os problemas que temos que conhecemos e que gostaríamos de resolver em primeiro lugar.



O primeiro e mais doloroso são as alterações na interface do usuário. Como estamos trabalhando com uma caixa preta, não incorporamos nada no aplicativo War Robots, exceto nosso servidor - ou seja, testamos tudo da mesma maneira que um engenheiro de controle de qualidade testaria. Mas, de alguma forma, precisamos acessar os elementos na cena. E nós os encontramos no caminho absoluto. Assim, quando algo muda no palco, especialmente em um alto nível de hierarquia, precisamos apoiar essas mudanças em um grande número de testes. Infelizmente, não podemos fazer nada com isso agora. Obviamente, existem algumas soluções, mas elas trazem problemas adicionais.

O segundo grande problema é a infraestrutura. Como eu disse, se você executar o aplicativo 50 vezes com as mãos, não perceberá a maioria dos problemas que aparecerão se executar o aplicativo 50 mil vezes. Esses problemas que podem ser facilmente resolvidos no modo manual - por exemplo, reinstalar compilações ou reiniciar a Internet - serão uma verdadeira dor de automação, porque todos esses problemas devem ser tratados corretamente, uma mensagem de erro exibida e desde que possam ocorrer. Em particular, precisamos determinar por que os testes foram reprovados: devido a uma lógica defeituosa ou algum tipo de problema de infraestrutura, ou por qualquer outro motivo. Existem muitos problemas com dispositivos low-end: eles não têm builds, a Internet cai, os dispositivos congelam, travam, não ligam, são descarregados rapidamente e assim por diante.

Também gostaria de interagir com as interfaces de usuário nativas, mas até agora não temos essa oportunidade. Sabemos como fazer isso, mas a presença de outros pedidos de funcionalidade não nos permite chegar a isso.

E, pessoalmente, meu desejo é cumprir os padrões existentes no setor, mas isso também está nos planos para o futuro, talvez até este ano.

All Articles