Como manter o tronco estável quando os testes demoram muito?

9

Temos três conjuntos de suítes de teste:

  • Uma suíte "pequena", levando apenas algumas horas para ser executada
  • Uma suíte "média" que leva várias horas, geralmente é executada todas as noites (todas as noites)
  • Uma suíte "grande" que leva mais de uma semana para ser executada

Também temos vários conjuntos de testes mais curtos, mas não estou focando neles aqui.

A metodologia atual é executar o pequeno conjunto antes de cada confirmação no tronco. Em seguida, o pacote médio é executado todas as noites e, se de manhã falhar, tentamos isolar qual dos commit de ontem foi o culpado, a reversão que confirma e repete os testes. Um processo semelhante, apenas com frequência semanal e não noturna, é realizado para o grande conjunto.

Infelizmente, o pacote médio falha com bastante frequência. Isso significa que o tronco geralmente é instável, o que é extremamente irritante quando você deseja fazer modificações e testá-las. É irritante porque, quando saio do porta-malas, não posso ter certeza de que é estável e, se um teste falhar, não posso ter certeza se a culpa é minha ou não.

Minha pergunta é: existe alguma metodologia conhecida para lidar com esse tipo de situação de uma maneira que deixe o tronco sempre na melhor forma? por exemplo, "comprometa-se em um ramo especial de pré-confirmação, que atualizará periodicamente o tronco toda vez que a noite passar".

E importa se é um sistema de controle de fonte centralizado como o SVN ou um sistema distribuído como o git?

Por ser um desenvolvedor júnior com uma capacidade limitada de mudar as coisas, estou apenas tentando entender se há uma maneira de lidar com essa dor que estou enfrentando.

Carvalho
fonte
6
Não tenho idéia de qual software você está trabalhando, mas um pequeno conjunto de testes que leva horas para ser executado é um pouco de WTF. Se eles rodassem mais rápido, isso seria mais fácil, não há como otimizar seus testes?
22712 Benjamin Bannier
2
o que é tão "extremamente irritante" sobre o tronco ser instável? Não sei se você sabe, mas uma das mais populares estratégias de ramificação é até chamado tronco instável
mosquito
11
Existem várias maneiras de otimizar um conjunto de testes (como em qualquer outro software). Não sei por que o seu demora tanto, mas você pode, por exemplo, reutilizar parte do ambiente de teste ou simplesmente usar melhores algoritmos / estruturas de dados ao executar (ajuda na criação de perfil). Também pode ser que ninguém tenha tempo para identificar amostras representativas e você apenas teste todas as entradas / saídas possíveis com base em alguma referência. Talvez seu sistema de compilação permita codificar dependências de teste de código para que você não precise executar o conjunto completo. E sei que essa não foi sua pergunta, por isso fiz um comentário, não uma resposta.
22712 Benjamin Bannier
11
... hmm, é provável que sua melhor opção melhore os testes e o log de aplicativos, para que seja mais fácil encontrar o motivo da falha. Dessa forma, seria necessário para encontrar e corrigir a causa da falha em vez de desperdiçar esforços em "investigações policiais" à procura de quem, quando e por que mudou determinada linha de código ...
mosquito
11
@honk Alguns testes levam muito tempo para serem executados. Eu trabalho para uma empresa que fabrica equipamentos de aquisição de dados e nosso teste "parcial" leva cerca de uma hora. Os testes precisam fazer vários tipos de medidas e isso leva tempo.
precisa

Respostas:

1

A única maneira de corrigir a causa raiz da instabilidade é desacoplar o código para que as alterações sejam mais isoladas, como sugeriram outras respostas.

No entanto, como desenvolvedor individual, se você deseja uma construção mais estável para trabalhar pessoalmente, é relativamente fácil de resolver. Em vez de trabalhar fora da dica, você apenas puxa a última compilação que passou no conjunto de testes noturnos para sua árvore de trabalho. Se você pode criar ramificações de recursos para cada alteração, ramifique a última compilação estável.

Sim, sua árvore ficará alguns dias atrasada, mas na maioria das vezes isso não importa. Faça seu trabalho com a construção estável, para que você saiba que foram as alterações que quebraram os testes; antes de fazer o check-in, atualize para o mais recente e faça sua integração normal. Depois de fazer o check-in, faça backup novamente na última versão estável.

Você ainda precisa fazer o trabalho de integração confuso, mas o que eu mais gosto nesse método é isolar o trabalho de integração para um momento mais conveniente para mim e fornecer uma base de código estável para o desenvolvimento, quando não for conveniente. Tenho uma ideia muito melhor quando são minhas alterações que provavelmente quebraram a compilação versus as de outra pessoa.

Karl Bielefeldt
fonte
11
-1 trabalhar a partir de ramificações é uma opção viável, mas recomendá-la sem sugestão de teste pode fazer mais mal do que bem. Somente os testes podem mostrar se é viável para um projeto específico ou não. Por exemplo, em um projeto que fiz cerca de 2 anos atrás, esse teste mostrou que o trabalho em filiais estava exigindo cerca de 7 vezes mais esforços em comparação com o tronco instável
gnat
Obrigado Karl! Embora não fosse isso que eu esperava aprender, essa é uma abordagem muito prática que pode me ajudar a resolver o problema em questão. E concordo que trabalhar alguns dias atrás do tronco raramente causará problemas de integração.
Oak
12

Sei que você está tentando evitar isso, mas o verdadeiro insight aqui é perceber que algo está seriamente errado com sua base de código: você precisa executar um conjunto completo de testes que leva uma semana apenas para garantir que seu código esteja estável!

A maneira mais vantajosa de corrigir esse problema é começar a separar sua base de código e os testes em subunidades (independentes).
Existem enormes vantagens para isso:

  • Os testes para cada uma dessas unidades serão executados mais rapidamente (são simplesmente poucos) e não serão interrompidos se algo der errado em uma das unidades independentes ou a jusante.
  • Um teste que falhou será apontado para uma unidade específica, o que facilitará a localização da origem do problema.
  • Você pode separar os locais VCS das diferentes unidades para que sua ramificação "estável" possa ser uma mistura da mais recente compilação testada com êxito de cada unidade, para que uma ou duas unidades quebradas não desestabilize sua versão "estável" .

No gerenciamento de outro lado da sua estrutura de VCS, ficará mais complicado, mas em uma semana inteira para o teste completo, acho que você pode aguentar a dor!

Eu ainda recomendo usar uma estratégia de ramos "estável" e "de desenvolvimento" de uma forma ou de outra, mas há muitas maneiras de fazer isso e você pode escolher a que melhor funciona para sua organização (meta-repositórios com revisões fixas apontando para repositórios separados para cada unidade, um ramo estável e um ramo dev, apresentam ramos ....)

Joris Timmermans
fonte
11
Eu nunca disse que o teste grande é um teste atômico, é uma suíte de testes . Quando um desenvolvedor individual faz uma modificação no elemento X, ele executa os testes relevantes para o X - independentemente de qual conjunto de testes eles se originaram. Isso é um complemento ao teste semanal, que é executado para garantir que uma alteração em um local não tenha afetado inesperadamente outro local. Mas você afirma que, pelo menos, separar as coisas dessa maneira ajudará a acelerar os testes para módulos específicos, mantendo o risco aproximadamente no mesmo nível.
Oak
2
@oak - Bem, de certa forma, a suíte é atômica se a execução de tudo é a única maneira de você realmente confiar que o código é estável, mas você faz um bom argumento, então eu editei minha resposta.
Joris Timmermans
4
Temos enormes testes para nossos compiladores, alguns dos quais levam vários dias para serem executados, e não acho que isso seja incomum para softwares tão complexos quanto um compilador C ++. Não é que o conjunto defina o que deve ser considerado como "estável", mas sim que existem milhões de bases de dados diferentes de geração de código que é impossível testá-las todos os dias.
JesperE
11
@Espere - isso é compreensível, se o enorme conjunto de testes não define "estável", mas é um teste de sanidade gigantesco. Eu não esperaria que o conjunto completo (ou mesmo o conjunto médio) falhasse com muita frequência.
Joris Timmermans #
1

Para o SVN, eu não sei sobre algo como "pré-confirmação". Eu acho que é provável que produza commits e rollbacks quando o teste falhar. Como doc-brown diz, a única maneira de confirmar em um ramo temporário e mesclá-lo com o tronco mais tarde.

Usando um distribuído como git ou mercurial, acho que seria possível. Usando um repositório "testing" e um repositório "stable". Você pressiona o representante de teste, o teste todas as noites e, se tudo correr bem, você pressiona de teste para estável. Caso contrário, você reverterá o representante de teste. Estou um pouco inseguro de como seria o histórico da versão quando você passar do teste para o estável, mas acho que é possível excluir as coisas de reversão quebradas ao fazer isso. Um pouco de experimentar primeiro seria o mais seguro.

Uma alternativa também seria testar o tronco local de cada pessoa todas as noites. Em seguida, as pessoas com testes aprovados podem enviá-lo para o servidor central pela manhã.

dagnelies
fonte
1

IMHO isso não tem nada a ver com o VCS que você está usando. O uso de uma ramificação "em teste" pode ser uma solução, que também pode ser realizada com VCS centralizado ou distribuído. Mas, honestamente, acho que a melhor coisa em sua situação é tentar otimizar o conjunto de testes médios (parece que ele contém os testes mais importantes) para que ele seja executado muito mais rapidamente e, portanto, você pode usá-lo para pré-confirmar com o tronco testes, assim como você faz agora com o seu "pequeno conjunto".

Doc Brown
fonte
Estou perguntando principalmente sobre metodologia aqui - ou seja, existe uma maneira comum de lidar com essa situação. Vamos supor, pelo menos para o propósito desta discussão, que os testes não podem ser otimizados além do que já são.
Oak
@ Oak: alguém aqui (você?) Votou na minha resposta - mas às vezes as coisas que você não quer ouvir são as únicas coisas que ajudarão. Como você vê na discussão abaixo da sua pergunta, outros sugeriram o mesmo, então minha sugestão não parece tão ruim.
Doc Brown
+1, esta é a resposta certa. A verdadeira questão do OP é "Ajuda, estou me afogando na porcaria, que metodologia posso usar para me ajudar?" e a resposta é que realmente a metodologia não é com o que você deveria se preocupar.
MrFox
1

Os testes com falha no meio: é verdade que na maioria das vezes os mesmos testes falham?

Se houver uma falha, há sempre os mesmos testes relacionados que falham?

Se verdadeiro: pode ser que você possa escolher seletivamente alguns testes médios que geralmente falham (um teste para cada classe de erro) e executá-los dentro do pequeno conjunto.

A maioria dos testes são testes de integração que usam um banco de dados real? Em caso afirmativo, é possível substituí-los por um unittest que possua um banco de dados falso?

k3b
fonte
1

Você precisa fazer seus testes mais rapidamente, não há outra maneira de enquadrar esse círculo.

Considere o problema: você quer ter certeza de que, ao fazer o check-out, possui um código de trabalho. Claro, você pode atrasar confirmações e ramificações até antes do lançamento, mas isso atrasará apenas o início do problema até a integração. Como você precisará executar o pacote de uma semana após cada mesclagem? Metodologia não é a solução, a solução é puramente técnica.

Aqui está o que eu sugiro:

1) Faça os testes o mais atômico possível e maximize a reutilização do ambiente.

2) Obtenha um farm de suíte de testes para executá-los. Se, em vez de oito módulos grandes, você terminar com 50, poderá gerar várias instâncias spot do Amazon EC2 e executar o conjunto inteiro em paralelo. Tenho certeza de que isso custará algum dinheiro, mas economizará enormes quantidades de tempo do desenvolvedor.

MrFox
fonte
0

A principal coisa que você está dando como certa na sua pergunta é que todas as confirmações devem passar nos testes. Embora seja uma boa regra a seguir e pareça fazer algum sentido, às vezes não é prático. Seu caso é um exemplo (embora MadKeithV faça alguma observação), e eu posso imaginar manter um ramo VCS tão intocado que poderia ser difícil se não houver cooperação suficiente entre os devlopers.

Na realidade, o que você quer é saber de alguma forma quais confirmações são aprovadas ou reprovadas. Uma "ramificação de pré-confirmação", como você sugeriu, funcionaria, mas isso pode exigir um esforço extra dos desenvolvedores quando eles fazem confirmações, o que pode ser difícil de vender.

Uma abordagem semelhante que poderia ser mais fácil é deixar o porta-malas para as pessoas quebrarem como quiserem e ter um ramo para confirmações que não estão quebradas. Um script automatizado pode passar por confirmações conforme são feitas no tronco, executar os testes neles e adicioná-los à ramificação, se eles passarem.

Ou você pode ser absurdamente simplista e ter um script que lista as confirmações de passagem em um arquivo de texto (que pode ou não ser controlado por versão).

Ou tenha um sistema em lote que aceite solicitações de ramificações / revisões para testar (de qualquer lugar da árvore), teste-as e as confirme no tronco (ou em outra ramificação) se elas forem aprovadas.

Michael Slade
fonte