É uma boa ideia medir o desempenho de um método usando o tempo limite do teste de unidade?

14

Em um projeto em que existem requisitos não funcionais que especificam o tempo máximo de execução para uma ação específica, o controle de qualidade deve verificar o desempenho dessa ação em uma máquina dedicada, usando hardware preciso sob carga precisa, tanto o hardware quanto a carga sendo especificados nos requisitos.

Por outro lado, algumas alterações incorretas no código-fonte podem afetar seriamente o desempenho. Perceber esse impacto negativo cedo , antes que o código-fonte atinja o controle de origem e seja verificado pelo departamento de controle de qualidade, pode ser benéfico em termos de tempo perdido pelo departamento de controle de qualidade que relata o problema e pelo desenvolvedor que o corrige várias confirmações posteriormente.

Para fazer isso, é uma boa ideia:

  • Para usar testes de unidade para ter uma idéia do tempo gasto executando a mesma ação² n vezes,

  • Para usar o tempo limite por teste através do [TestMethod, Timeout(200)]atributo em C #?

Espero vários problemas com essa abordagem:

  • Conceitualmente , os testes de unidade não são realmente para isso: eles devem testar uma pequena parte de um código, nada mais: nem a verificação de um requisito funcional, nem um teste de integração, nem um teste de desempenho.

  • O tempo limite do teste de unidade no Visual Studio mede realmente o que se espera que seja medido, levando em consideração que a inicialização e a limpeza não existem para esses testes ou são muito curtas para afetar os resultados?

  • Medir o desempenho dessa maneira é feio. Executar um benchmark em qualquer máquina¹ independentemente do hardware, carga, etc. é como fazer um benchmark que mostre que um produto de banco de dados é sempre mais rápido que outro. Por outro lado, não espero que esses testes de unidade sejam um resultado definitivo, nem algo usado pelo departamento de controle de qualidade . Esses testes de unidade serão usados ​​apenas para fornecer uma idéia geral sobre o desempenho esperado e, essencialmente, para alertar o desenvolvedor de que sua última modificação quebrou algo, afetando severamente o desempenho .

  • O Test Driven Development (TDD) é impossível para esses testes. Como isso falharia, em primeiro lugar, antes de começar a implementar o código?

  • Muitos testes de desempenho afetarão o tempo necessário para executar os testes, portanto, essa abordagem é limitada apenas a ações curtas.

Levando em conta esses problemas, ainda acho interessante usar esses testes de unidade se combinados com as métricas reais de desempenho do departamento de controle de qualidade.

Estou errado? Existem outros problemas que tornam totalmente inaceitável o uso de testes de unidade para isso?

Se eu estiver errado, qual é a maneira correta de alertar o desenvolvedor de que uma alteração no código-fonte afetou gravemente o desempenho, antes que o código-fonte alcance o controle de origem e seja verificado pelo departamento de controle de qualidade?


¹ Na verdade, espera-se que os testes de unidade sejam executados apenas em PCs desenvolvedores com desempenho de hardware comparável, o que reduz a diferença entre as máquinas mais rápidas que nunca serão capazes de falhar no teste de desempenho e as máquinas mais lentas que nunca conseguirão passar por ele.

² Por ação, quero dizer um pedaço de código bastante curto que gasta alguns milissegundos para executar.

Arseni Mourzenko
fonte

Respostas:

3

Também estamos usando essa abordagem, ou seja, temos testes que medem o tempo de execução em algum cenário de carga definido em uma determinada máquina. Pode ser importante ressaltar que não os incluímos nos testes de unidade normais. Os testes de unidade são basicamente executados por cada desenvolvedor em uma máquina do desenvolvedor antes de confirmar as alterações. Veja abaixo por que isso não faz sentido para testes de desempenho (pelo menos no nosso caso). Em vez disso, executamos testes de desempenho como parte dos testes de integração.

Você apontou corretamente que isso não deve excluir a verificação. Não assumimos que nosso teste seja um teste do requisito não-funcional. Em vez disso, consideramos um mero indicador de potencial problema.

Não tenho certeza do seu produto, mas, no nosso caso, se o desempenho for insuficiente, significa que é necessário muito trabalho para "consertar" isso. Portanto, o tempo de retorno, quando deixamos isso inteiramente para o controle de qualidade, é horrível. Além disso, as correções de desempenho terão impactos graves em grande parte da base de código, o que torna o trabalho de controle de qualidade anterior nulo. Em suma, um fluxo de trabalho muito ineficiente e insatisfatório.

Dito isto, aqui estão alguns pontos para seus respectivos problemas:

  • conceitualmente: é verdade que não é disso que se trata os testes de unidade. Mas desde que todos saibam que o teste não deve verificar nada que o controle de qualidade deva fazer, tudo bem.

  • Visual Studio: não posso dizer nada sobre isso, pois não usamos a estrutura de teste de unidade do VS.

  • Máquina: Depende do produto. Se o seu produto é algo desenvolvido para usuários finais com computadores individuais personalizados, é mais realista executar os testes em computadores de diferentes desenvolvedores. No nosso caso, entregamos o produto para uma máquina com uma determinada especificação e executamos esses testes de desempenho apenas nessa máquina. De fato, não há muito sentido em medir o desempenho em sua máquina de desenvolvedor de núcleo duplo, quando o cliente finalmente executará 16 núcleos ou mais.

  • TDD: Embora a falha inicial seja típica, não é uma obrigação. De fato, escrever esses testes antecipadamente faz com que seja mais um teste de regressão do que um teste de unidade tradicional. Que o teste seja bem-sucedido desde o início não é problema. Mas você obtém a vantagem de que sempre que um desenvolvedor adiciona funcionalidades que atrasam as coisas, porque ele não estava ciente do requisito de desempenho não funcional, esse teste de TDD o identifica. Acontece muito, e é um feedback incrível. Imagine isso em seu trabalho diário: você escreve código, o confirma, vai almoçar e, quando volta, o sistema de compilação informa que esse código, quando executado em um ambiente de carga pesada, é muito lento. Isso é bom o suficiente para eu aceitar, que o teste TDD não foi inicialmente reprovado.

  • Tempo de execução: Como mencionado, não executamos esses testes em máquinas de desenvolvedor, mas como parte do sistema de compilação em uma espécie de teste de integração.

Frank
fonte
3

Estou principalmente alinhado com o seu pensamento. Apenas colocando meu raciocínio com fluxo independente.

1. Faça o trabalho antes de melhorá-lo / mais rápido
Antes que o código forneça qualquer medida de desempenho (sem falar na garantia), ele deve primeiro ser corrigido, ou seja, torná-lo funcional. A otimização de código que está funcionalmente errado não é apenas perda de tempo, mas coloca impedimentos no desenvolvimento.

2. O desempenho de um sistema faz sentido apenas no sistema completo
Normalmente, qualquer desempenho significativo sempre depende de uma determinada infraestrutura e deve ser visto apenas em um sistema completo. Por exemplo, durante o teste simulado, se o módulo recebe respostas de arquivos de texto locais, mas no ambiente de produção ele busca no banco de dados, você

3. A escala de desempenho deve ser feita por objetivo
Depois de ter o sistema funcional, você precisa analisar o desempenho do sistema e encontrar gargalos para entender onde você precisa aumentar o desempenho. Tentar cegamente otimizar todos os métodos, mesmo antes que você saiba que o desempenho de um sistema completo pode resultar em uma quantidade inútil de trabalho (otimizar métodos que não importam) e pode criar seu código desnecessariamente inchado.

Não estou ciente da funcionalidade do Visual studio, mas geralmente você precisa de uma ferramenta de criação de perfil mais ampla.

Dipan Mehta
fonte
2

Eu tive uma tarefa semelhante há algum tempo e a solução final estava em algum lugar entre o teste de unidade e o teste de desempenho automatizado completo.

Algumas considerações em nenhuma ordem específica, que podem ser úteis:

  • O teste de desempenho pelo controle de qualidade exigia muito trabalho e tinha seu próprio cronograma (digamos, uma vez na iteração), portanto, controlar o controle de origem não era um problema.
  • Nosso sistema era grande e modular, os testes de unidade eram muito granulares para nossas necessidades e criamos testes de unidade especiais "gordos" cuidadosamente criados para desencadear problemas de desempenho em áreas específicas de interesse (eles também foram categorizados, mas isso é um detalhe de implementação).
  • As restrições usuais para testes de unidade ainda se aplicam: elas devem ser pequenas, rápidas e objetivas.
  • Para excluir a influência da estrutura de teste, eles estavam sendo executados por um invólucro especial; portanto, sabíamos exatamente quanto tempo a operação fornecida leva.
  • É possível escrevê-los antes que a implementação real seja concluída (os resultados podem ser irrelevantes ou úteis, dependendo do processo, talvez os desenvolvedores ainda estejam experimentando a implementação e gostariam de ver como ela está indo).
  • Eles estavam sendo executados pelo servidor de IC após cada compilação, portanto, o tempo total de execução deve ser relativamente curto (se não for assim, fica consideravelmente mais difícil identificar a alteração exata que desencadeou o problema).
  • O servidor de CI era poderoso e tinha seu hardware corrigido; portanto, contamos isso como uma máquina dedicada (é possível usar um servidor realmente dedicado usando um agente de construção remoto).
  • O wrapper de teste coletou todas as informações relevantes (especificações de hardware, nomes / categorias de teste, carregamento do sistema, tempo decorrido etc.) e as exportou como relatórios ou para o banco de dados.
  • Tivemos um gadget para o JIRA puxando esses relatórios e desenhando gráficos agradáveis ​​por nome / categoria / número de compilação com alguns controles (sobreponha a versão anterior à atual, etc.), para que os desenvolvedores possam ver rapidamente seu impacto e os gerentes possam obter uma visão geral (alguns vermelhos, todos verdes, você sabe, é importante para eles).
  • Foi possível analisar como o projeto está indo ao longo do tempo, usando as estatísticas coletadas.

Portanto, no final, tínhamos um sistema escalável, flexível e previsível, que podemos ajustar rapidamente para nossos requisitos especiais. Mas exigiu algum esforço para implementar.

Voltando às perguntas. Conceitualmente , os testes de unidade não são para isso, mas você pode aproveitar os recursos da sua estrutura de testes. Nunca vi os intervalos de teste como um meio de medir, é apenas uma rede de segurança para travamentos e coisas assim. Mas se sua abordagem atual funcionar para você, continue a usá-la, seja prático. Você sempre pode ir à moda depois, se necessário.

Oleg Kolosov
fonte
0

Eu acho que você está bem. Este é exatamente o ponto de ter intervalos de teste de unidade: para verificar se algo está acontecendo , muito mais tempo do que deveria. Existem limitações para essa abordagem, mas você parece já estar ciente delas. Portanto, desde que você mantenha essas limitações em mente, não vejo problema.

Mike Baranczak
fonte