Chegamos ao ponto em nosso projeto em que temos quase mil testes e as pessoas pararam de se preocupar em executá-los antes de fazer o check-in, porque leva muito tempo. Na melhor das hipóteses, eles executam os testes relevantes para o trecho de código que eles mudaram e, na pior das hipóteses, simplesmente fazem o check-in sem testar.
Acredito que esse problema se deva ao fato de a solução ter aumentado para 120 projetos (geralmente fazemos projetos muito menores e esta é apenas a segunda vez que fazemos o TDD corretamente) e o tempo de compilação + teste aumentou para cerca de dois a três minutos nas máquinas menores.
Como reduzimos o tempo de execução dos testes? Existem técnicas? Fingindo mais? Fingindo menos? Talvez os testes de integração maiores não devam ser executados automaticamente ao executar todos os testes?
Editar: como resposta a várias respostas, já usamos o CI e um servidor de compilação, é assim que sei que os testes falham. O problema (na verdade, um sintoma) é que continuamos recebendo mensagens sobre compilações com falha. A execução de testes parciais é algo que a maioria das pessoas faz, mas não todas. e em relação aos testes, eles são realmente muito bem feitos, usam falsificações para tudo e não há IO.
fonte
Respostas:
Uma solução possível seria mover a parte de teste das máquinas de desenvolvimento para uma configuração de integração contínua ( Jenkins, por exemplo), usando um software de controle de versão de algum sabor ( git , svn , etc ...).
Quando um novo código tiver que ser escrito, o desenvolvedor especificado criará uma ramificação para o que estiver fazendo no repositório. Todo o trabalho será realizado neste ramo e eles poderão confirmar suas alterações no ramo a qualquer momento, sem prejudicar a linha principal de código.
Quando o recurso fornecido, a correção de bug ou o que quer que esteja trabalhando, é concluído que a ramificação pode ser mesclada de volta ao tronco (ou como você preferir) onde todos os testes de unidade são executados. Se um teste falhar, a mesclagem será rejeitada e o desenvolvedor será notificado para que eles possam corrigir os erros.
Você também pode fazer com que o servidor de IC execute os testes de unidade em cada ramificação de recurso à medida que as confirmações são feitas. Dessa forma, o desenvolvedor pode fazer algumas alterações, confirmar o código e permitir que o servidor execute os testes em segundo plano enquanto continua trabalhando em alterações adicionais ou outros projetos.
Um ótimo guia para uma maneira de fazer essa configuração pode ser encontrado aqui (específico ao git, mas deve funcionar para outros sistemas de controle de versão): http://nvie.com/posts/a-successful-git-branching-model/
fonte
A maioria dos testes de unidade deve levar menos de 10 milissegundos cada um. Tendo 'quase mil testes' é nada e deve levar talvez alguns segundos para ser executado.
Se não estiverem, pare de escrever testes de integração altamente acoplados (a menos que seja o que o código precise) e comece a escrever bons testes de unidade (começando com código bem dissociado e uso adequado de fakes / mocks / stubs / etc). Esse acoplamento afetará a qualidade do teste e o tempo necessário para escrevê-los - portanto, não se trata apenas de reduzir o tempo de execução do teste.
fonte
py.test
) faz toneladas de mágica em segundo plano e tudo é puro código Python ("100x mais lento que C "), a execução dos cerca de 500 testes em um projeto meu leva menos de 6 segundos em um netbook lento de vários anos. Este número é aproximadamente linear no número de testes; embora exista alguma sobrecarga de inicialização, ela é amortizada em todos os testes e a sobrecarga por teste é O (1).Existem várias abordagens que usei para resolver um problema semelhante:
Além disso, você pode usar as seguintes ferramentas para facilitar sua vida e executar os testes mais rapidamente
pnunit
e configuração de IC com vários nós.fonte
0. Ouça seus programadores.
Se eles não estão executando os testes, significa que eles percebem que o custo (aguardando a execução dos testes, lidando com falhas falsas) é maior que o valor (detectando bugs imediatamente). Diminua os custos, aumente o valor e as pessoas farão os testes o tempo todo.
1. Faça seus testes 100% confiáveis.
Se você tiver testes que falham com falsos negativos, lide com isso imediatamente. Corrija-os, altere-os, elimine-os, o que for necessário para garantir 100% de confiabilidade. (Não há problema em ter um conjunto de testes não confiáveis, mas ainda úteis, que você pode executar separadamente, mas o corpo principal dos testes deve ser confiável.)
2. Altere seus sistemas para garantir que todos os testes passem o tempo todo.
Use sistemas de integração contínua para garantir que apenas as confirmações passantes sejam mescladas à ramificação principal / oficial / de liberação / qualquer que seja.
3. Mude sua cultura para valorizar 100% de aprovação nos testes.
Ensine a lição que uma tarefa não é "concluída" até que 100% dos testes passem e ela foi mesclada à ramificação principal / oficial / de liberação / qualquer que seja.
4. Faça os testes rapidamente.
Trabalhei em projetos em que os testes demoram um segundo e em projetos em que eles duram o dia todo. Existe uma forte correlação entre o tempo que leva para executar os testes e minha produtividade.
Quanto mais testes demoram para ser executados, menos vezes você os executa. Isso significa que você ficará mais tempo sem receber feedback sobre as alterações que está fazendo. Isso também significa que você passará mais tempo entre as confirmações. Confirmar com mais frequência significa etapas menores que são mais fáceis de mesclar; confirmar o histórico é mais fácil de seguir; encontrar um bug na história é mais fácil; retroceder também é mais fácil.
Imagine testes que executam tão rápido que você não se importa de executá-los automaticamente toda vez que compila.
Fazer testes rapidamente pode ser difícil (foi o que o OP pediu, certo!). Desacoplamento é a chave. Zombarias / falsificações estão OK, mas acho que você pode fazer melhor refatorando para tornar desnecessárias as zombarias / falsificações. Veja o blog de Arlo Belshee, começando com http://arlobelshee.com/post/the-no-mocks-book .
5. Faça testes úteis.
Se os testes não falham quando você estraga tudo, então qual é o objetivo? Ensine-se a escrever testes que detectem os erros que você provavelmente criará. Esta é uma habilidade em si mesma e exigirá muita atenção.
fonte
Alguns minutos são bons para testes de unidade. No entanto, lembre-se de que existem três tipos principais de testes:
Estes estão listados em ordem de velocidade. Os testes de unidade devem ser rápidos. Eles não detectam todos os erros, mas estabelecem que o programa é decentemente são. Os testes de unidade devem ser executados em 3 minutos ou menos ou com um hardware decente. Você diz que só tem 1000 testes de unidade e eles levam de 2 a 3 minutos? Bem, provavelmente está tudo bem.
Coisas a verificar:
Certifique-se de garantir que seus testes de unidade e testes de integração sejam separados. Os testes de integração sempre serão mais lentos.
Verifique se os testes de unidade estão sendo executados em paralelo. Não há motivo para não fazerem se são verdadeiros testes de unidade
Verifique se seus testes de unidade são "livres de dependência". Eles nunca devem acessar um banco de dados ou o sistema de arquivos
Fora isso, seus testes não parecem muito ruins agora. No entanto, para referência, um dos meus amigos de uma equipe da Microsoft tem 4.000 testes de unidade que são executados em menos de 2 minutos em hardware decente (e é um projeto complicado). É possível fazer testes de unidade rápidos. Eliminar dependências (e simular apenas o necessário) é a principal coisa para obter velocidade.
fonte
Treine seus desenvolvedores no Personal Software Process (PSP), ajudando-os a entender e melhorar seu desempenho usando mais disciplina. Escrever código não tem nada a ver com bater os dedos em um teclado e depois pressionar um botão de compilação e check-in.
O PSP costumava ser muito popular no passado quando a compilação de código era um processo que demorava muito tempo (horas / dias em um mainframe para que todos tivessem que compartilhar o compilador). Mas quando as estações de trabalho pessoais se tornaram mais poderosas, todos nós aceitamos o processo:
Se você pensa antes de digitar e, depois de digitar, revise o que escreveu, poderá reduzir o número de erros antes de executar um conjunto de compilação e teste. Aprenda a não pressionar a compilação 50 vezes por dia, mas talvez uma ou duas vezes, então é menos importante que o tempo de compilação e teste demore mais alguns minutos.
fonte
Uma maneira possível: divida sua solução. Se uma solução possui 100 projetos, é bastante incontrolável. Só porque dois projetos (digamos A e B) usam algum código comum de outro projeto (digamos Lib) não significa que eles precisam estar na mesma solução.
Em vez disso, você pode criar a solução A com os projetos A e Lib e também a solução B com os projetos B e Lib.
fonte
Estou em uma situação similar. Eu tenho testes de unidade que testam a comunicação com o servidor. Eles estão testando o comportamento com tempos limite, cancelando conexões etc. Todo o conjunto de testes dura 7 minutos.
7 minutos é um período relativamente curto, mas não é algo que você fará antes de cada confirmação.
Também temos um conjunto de testes de interface do usuário automatizados, cujo tempo de execução é de 2 horas. Não é algo que você deseja executar todos os dias no seu computador.
Então o que fazer?
O importante é: todos os seus testes devem ser executados com frequência, porque é importante encontrar os bugs. No entanto, não é absolutamente necessário encontrá-los antes do commit.
fonte
Embora sua descrição do problema não forneça uma visão completa da base de código, acho que posso dizer com segurança que seu problema é duplo.
Aprenda a escrever os testes certos.
Você diz que tem quase mil testes e tem 120 projetos. Supondo que no máximo metade desses projetos sejam projetos de teste, você tem 1000 testes para 60 projetos de código de produção. Isso fornece cerca de 16 a 17 testes pr. projeto!!!
Essa é provavelmente a quantidade de testes que eu teria que cobrir cerca de uma a duas classes em um sistema de produção. Portanto, a menos que você tenha apenas 1-2 classes em cada projeto (nesse caso, a estrutura do seu projeto é muito refinada), seus testes são muito grandes, eles cobrem muito terreno. Você diz que este é o primeiro projeto que você está executando o TDD corretamente. Por exemplo, os números que você apresenta indicam que esse não é o caso, você não está fazendo a propriedade TDD.
Você precisa aprender a escrever os testes certos, o que provavelmente significa que você precisa aprender a tornar o código testável em primeiro lugar. Se você não encontrar a experiência dentro da equipe para fazer isso, sugiro contratar ajuda externa, por exemplo, na forma de um ou dois consultores ajudando sua equipe por um período de 2 a 3 meses para aprender a escrever código testável e pequenos testes unitários mínimos.
Como comparação, no projeto .NET em que estou trabalhando atualmente, podemos executar aproximadamente 500 testes de unidade em menos de 10 segundos (e isso nem foi medido em uma máquina de alta especificação). Se esses fossem seus números, você não teria medo de executá-los localmente de vez em quando.
Aprenda a gerenciar a estrutura do projeto.
Você dividiu a solução em 120 projetos. Pelos meus padrões, é uma quantidade impressionante de projetos.
Portanto, se faz sentido ter realmente essa quantidade de projetos (o que acho que não tem - mas sua pergunta não fornece informações suficientes para fazer um julgamento qualificado disso), você precisa dividir os projetos em componentes menores que pode ser compilado, com versão e implantado separadamente. Portanto, quando um desenvolvedor executa a unidade do conjunto de testes, ele só precisa executar os testes relacionados ao componente em que está trabalhando atualmente. O servidor de compilação deve verificar se tudo se integra corretamente.
Mas dividir um projeto em múltiplos componentes, compilado, com versão e implantado separadamente exige, em minha experiência, uma equipe de desenvolvimento muito madura, uma equipe mais madura do que eu sinto que sua equipe é.
Mas, de qualquer forma, você precisa fazer algo sobre a estrutura do projeto. Divida os projetos em componentes separados ou comece a mesclar projetos.
Pergunte a si mesmo se você realmente precisa de 120 projetos?
ps Você pode querer conferir o NCrunch. É um plug-in do Visual Studio que executa seu teste automaticamente em segundo plano.
fonte
O teste JUnit normalmente deve ser rápido, mas alguns deles precisam levar algum tempo para serem executados.
Por exemplo, o teste do banco de dados geralmente leva alguns minutos para inicializar e concluir.
Se você tiver centenas de testes, mesmo que sejam rápidos, eles exigirão muito tempo para serem executados devido ao seu número.
O que pode ser feito é:
1) Identifique os testes cruciais. Aqueles para as partes mais importantes das bibliotecas e aqueles com maior probabilidade de falhar após as alterações. Somente esses testes devem ser executados sempre na compilação. Se algum código for quebrado com frequência, seus testes deverão ser obrigatórios, mesmo que demorem muito para serem executados, por outro lado, se alguma parte do software nunca causou um problema, você pode pular com segurança os testes em cada build.
2) Prepare o servidor de integração contínua, que executará todos os testes em segundo plano. Depende de você se você decidir construir a cada hora ou após cada confirmação (o segundo só fará sentido se você quiser detectar automaticamente cuja confirmação causou problemas).
fonte
Problemas que eu já vi:
a) Usando o COI para criar elementos de teste. 70 segundos -> 7 segundos removendo o Container.
b) Não zombando de todas as classes. Mantenha seus testes de unidade em um único elemento. Eu já vi testes que divagam através de algumas aulas. Estes não são testes de unidade e são muito mais propensos a quebrar.
c) Crie um perfil para descobrir o que estava acontecendo. Eu achei que o construtor estava construindo coisas que eu não precisava, então localizei e reduzi os tempos de execução.
d) perfil. talvez o código não seja tão bom e você possa obter alguma eficiência com uma revisão.
e) Remova as dependências. Manter o teste executável pequeno reduzirá o tempo de carregamento. Use uma biblioteca de interfaces e contêineres IOC para executar sua solução final, mas seus principais projetos de teste devem ter apenas a biblioteca de interfaces definida. Isso garante a separação, facilita o teste e também diminui a impressão do pé de teste.
fonte
Sinto sua dor e já corri em vários lugares onde a velocidade de construção pode ser bastante melhorada. No entanto, o número de coisas que recomendo é medir em detalhes granulares para descobrir onde a sua construção está demorando mais. Por exemplo, eu tenho uma compilação com cerca de 30 projetos que levam pouco mais de um minuto para serem executados. No entanto, isso é apenas parte da imagem. Também sei quais projetos demoram mais para serem construídos, o que ajuda a concentrar meus esforços.
Coisas que consomem tempo de construção:
As bibliotecas simuladas usam reflexão ou injetam código usando as bibliotecas bytecode para gerar a simulação para você. Embora seja muito conveniente, consome tempo de teste. Se você estiver gerando zombarias dentro de um loop em seu teste, isso poderá adicionar uma quantidade mensurável de tempo aos testes de unidade.
Existem maneiras de corrigir os problemas:
Quando sua solução contém mais de 100 projetos, você tem uma combinação de código de biblioteca, testes e código de aplicativo. Cada uma das bibliotecas pode ser sua própria solução com seus testes associados. O Jet Brains Team City é um servidor de criação de CI que funciona como um servidor Nuget - e tenho certeza de que não é o único. Isso oferece a flexibilidade de mover as bibliotecas que provavelmente não são alteradas frequentemente para suas próprias soluções / projetos e usar o Nuget para resolver as dependências do código do aplicativo. Soluções menores significam que você pode fazer alterações em uma biblioteca rapidamente e sem problemas e aproveitar os benefícios da solução principal.
fonte
Seu ambiente de teste pode ser executado em qualquer lugar? Se possível, use a computação em nuvem para executar os testes. Divida os testes entre N máquinas virtuais. Se o tempo para executar os testes em uma única máquina for T1 segundos, o tempo para executá-los, T2, poderá se aproximar de T2 = T1 / N. (Supondo que cada caso de teste leve aproximadamente a mesma quantidade de tempo.) E você só precisará pagar pelas VMs quando as estiver usando. Portanto, você não tem um monte de máquinas de teste em algum laboratório em algum lugar 24/7. (Adoraria poder fazer isso onde trabalho, mas estamos vinculados a hardware específico. Não há VMs para mim.)
fonte