Ao ter uma integração contínua executando os testes em cada confirmação, uma prática recomendada comum é fazer com que todos os testes passem o tempo todo (também conhecido como "não quebre a compilação").
Encontro alguns problemas com isso:
Por exemplo, não se pode ajudar um projeto de código aberto criando testes correspondentes a tickets. Sei que se eu propuser uma solicitação de recebimento a um projeto de código aberto que contenha teste de falha, a compilação será marcada como falha e o projeto não desejará que ela seja mesclada em seu repositório porque "interromperá a compilação".
E não acredito que seja ruim ter testes reprovados em seu repositório , é como ter problemas em aberto no seu rastreador. Essas são apenas coisas que estão esperando para serem consertadas.
O mesmo vale para uma empresa. Se você trabalha com TDD, não pode escrever testes, confirmar e depois escrever o código lógico que realiza o teste. Isso significa que, se eu escrevi de 4 a 5 testes no meu laptop, não posso executá-los antes de sair de férias. Ninguém pode retomar o meu trabalho. Eu não posso nem "compartilhá-los" com um colega, exceto enviando-os por e-mail, por exemplo. Também impede trabalhar com uma pessoa escrevendo os testes e a outra escrevendo o modelo.
Tudo isso para dizer, estou usando mal / entendendo mal o processo de compilação / integração contínua? Parece-me que "passar" / "não passar" é um indicador muito restrito.
Existe uma maneira de tornar a integração contínua e compatível com TDD?
Talvez exista uma solução / prática padrão para distinguir "novos testes" (que podem falhar) e "testes de regressão" (que não devem falhar porque costumavam trabalhar)?
fonte
Respostas:
Vejo onde você está chegando, mas esses tipos de problemas geralmente são resolvidos de outras maneiras. Há uma boa razão pela qual esse é o protocolo padrão. Se alguém enviar um código que não seja compilado, todos que atualizarem seu código terão um programa que não será compilado . Isso inclui programadores que atualmente estão trabalhando em algo completamente diferente e, de alguma forma, se encontram em uma situação em que precisam esperar antes de poderem compilar e testar o que estão trabalhando.
O protocolo padrão é que você pode confirmar as alterações mesmo para o trabalho completo ou até incompleto, desde que seja compilado para que os programadores possam atualizar seu código todos os dias, se necessário.
No entanto, ainda vejo o que você está recebendo. Às vezes, você deseja confirmar para salvar seu código. Para isso, a maioria dos repositórios de origem suporta ramificação. Isso permite que você crie uma ramificação privada, trabalhe nela sem incomodar outras pessoas e, em seguida, entre no tronco quando o trabalho estiver concluído. Isso permite que você efetue o commit quando quiser, sem nenhuma das folgas associadas à quebra da compilação.
Se isso não for adequado, o GIT permite confirmar (enviando) para repositórios em sua máquina local, mas é possível que o repositório esteja em qualquer lugar. Você pode criar um repositório para trabalho potencialmente parcial / incompleto e outro repositório para o trabalho finalizado; nesse repositório, você pode adicionar uma construção noturna.
Mais uma vez, não posso enfatizar a importância o suficiente. Nunca cometa código quebrado no tronco! Suas contribuições não podem afetar o trabalho de outros programadores.
Editar
Vejo que você pretendia testes quebrados, mas, na minha humilde opinião, há pouca diferença. O objetivo de um teste é determinar se um aspecto específico de um programa passa ou falha. Se ele sempre falha e você não faz nada, o teste, no uso tradicional do teste de unidade, não serve para nada. Se você usá-lo para executar alguma outra métrica que não implica necessariamente uma confirmação "com falha" se um desses testes falhar, eu recomendo fortemente que você encontre outra maneira de fazer a mesma coisa.
Caso contrário, você corre o risco de o teste nunca ser levado em consideração ou, se causar falha na sua compilação, que seus colegas programadores ignorem as compilações com falha. É mais importante que os programadores percebam quando quebraram uma compilação do que realizar um teste que não oferece uma visão real e pode resultar apenas em práticas inadequadas.
fonte
the build would always have failing tests
precisamente! Mas isso é uma coisa tão ruim? Nossa única métrica é "a compilação está corrompida ou não", mas seu código pode estar cheio de bugs conhecidos , de modo que isso realmente não significa nada, exceto que não há regressão. Em um mundo perfeito, todos os problemas dos rastreadores teriam um teste (reproduzir é mais fácil do que consertar). Portanto, a vantagem seria ver que 35 testes / 70% de todos os testes estão passando, que o Branch-A o aprimora para 40 testes (80%) sem regressão e que o Branch-B tem regressões. Hoje, você só pode dizer que o Mestre e o Ramo A estão OK e o Ramo B está quebrado.Dada uma ramificação principal com testes falhos, como você pode ter certeza - sem comparar essa lista com compilações anteriores - que não introduziu bugs?
Simplesmente acompanhar o número de testes com falha é insuficiente: você pode corrigir um teste e interromper outro. E se você estiver de férias, não ficará claro para outras pessoas que estão olhando para a falha na construção.
Mantenha seu ramo mestre sempre limpo e verde . Trabalhe em um galho. Mantenha a filial sob o IC, em um trabalho separado, e faça testes reprovados no conteúdo do seu coração. Só não quebre o mestre.
Faça com que o revisor da ramificação apenas mescle sua ramificação se ela passar em todos os testes. (Mais fortemente: tenha o revisor capaz de mesclar sua ramificação se o resultado da fusão da ramificação no mestre passar em todos os testes!)
fonte
Simply tracking the number of failing tests is insufficient
essa não é a única métrica possível. Por exemplo:Branch-A improves it to 40 tests (80% passing) with no regression
. Nenhuma regressão significa que os testes anteriores sempre passam. Em resumo, um teste poderá falhar desde que nunca tenha passado. Parece-me que estamos perdendo coisas boas ao restringir a proibição de testes com falha nos ramos principais. (é claro que isso exigiria ferramentas de trabalho de forma diferente: testes unitários, CI, ...)Existem maneiras de resolver seus problemas sem descartar práticas bem compreendidas e aceitas sobre integração contínua.
Começarei com o problema de cometer um 'teste quebrado' que corresponde a um ticket. Uma solução é criar um ou mais testes de quebra expondo o problema e, na verdade, corrigi- lo, para que possam ser mesclados novamente à linha de código principal. A segunda solução é fazer os testes interrompidos, mas use algum tipo de sinalizador de ignorar para que eles não executem e quebrem a compilação. Possivelmente, adicione um comentário ou uma anotação especial que torne muito óbvio que esse é um teste quebrado
Ticket#N
. Anexe também uma nota ao próprio ticket que se refere aos testes criados que estão aguardando para serem ignorados e executados. Isso ajudaria uma pessoa a consertar o ticket, mas também não seria uma bandeira vermelha para alguém que passasse pelo teste.E para o seu próximo problema com o TDD. TDD é sobre escrever um pequeno teste e depois escrever um pequeno pedaço de código para fazer esse teste passar . Continue iterando até ter um pequeno módulo funcional. Eu sinto que se você escrever 4-5 testes e sair de férias, poderá estar fazendo errado. Você pode emparelhar o programa com alguém de uma maneira que um de vocês escreva o teste e o outro o código correspondente. No entanto, você não deve usar o repositório da linha de código principal para compartilhar esse código entre vocês dois antes que um módulo completo esteja pronto para ser confirmado. Como outros sugeriram, um ramo compartilhado resolveria seus problemas lá.
Tentar quebrar o mantra de integração contínua pode levar a caminhos inesperados e assustadores. Por exemplo, o que significaria cobertura de código nesse tipo de ambiente ? Como os desenvolvedores não acham que o sistema possui muitos " Windows Quebrados " ? Como alguém faria uma alteração, executaria o teste e saberia se eles estão realmente quebrando algo novo, ou se são apenas coisas antigas?
fonte
Eu acho que seu problema fundamental é que você está incluindo os resultados do teste como parte da compilação. Enquanto obviamente algumas pessoas concordam com você, outras não. Quebrar a compilação ocorre quando não é compilado. Não quando não cria sem erros.
Considere um projeto importante como o Windows ou Linux, ou mesmo algo como o Firefox - você acha que eles são enviados sem erros? Claro que não. Agora, esses projetos não estão executando o TDD, mas isso é realmente irrelevante - o TDD não muda dois fatos fundamentais: existem erros e leva tempo para corrigi-los. Tempo que um projeto (código aberto ou não) simplesmente não pode perder com bugs de baixa prioridade. O KDE recentemente corrigiu um bug com mais de uma década. Quando foi a última vez que você ouviu alguém dizer "Fico feliz que esperamos uma década para lançar nosso projeto"?
De certa forma, o TDD provavelmente facilita o envio de bugs - porque você entende melhor qual é a falha. Se você pode definir com precisão o que causa o erro, você tem uma excelente base para avaliar o custo de corrigi-lo.
Minha recomendação é encontrar um projeto que não se importe com o vermelho entre o verde.
fonte
Prefiro que todos os testes não falhem (não são vermelhos).
Com essa definição um pouco diferente, você também pode definir testes que são
Se você checá-las no repositório, sua construção contínua não está quebrada e, portanto, é válida.
fonte
Você pode considerar dois conceitos diferentes de construção de IC.
Uma criação de IC "futura". Essa compilação compila e executa apenas os testes para os quais nenhum código foi escrito especificamente para fazê-los passar. Pode haver vários motivos para fazer esse teste:
É possível adicionar testes para casos de falha específicos no rastreador de problemas, para os quais ainda não foi tentada nenhuma correção. É claramente útil já ter um teste em execução codificado para um problema, mesmo sem uma correção.
Testes adicionados para a nova funcionalidade necessária que ainda não foi implementada.
Freqüentemente, é mais fácil saber como testar uma falha ou recurso do que implementar ou corrigir o recurso, e separar as duas etapas submetendo o teste ao controle de origem pode ser útil para garantir que nenhuma informação seja perdida.
Como no IC "padrão", no regime de desenvolvimento regular, a equipe só observava os resultados diários da compilação regular.
A equipe também pode acompanhar a evolução dos casos de teste a partir da compilação "futura" do IC - especificamente para verificar se alguma alteração feita no IC regular realmente corrige problemas da compilação "futura", o que pode ser uma indicação de uma importante problema subjacente ou melhoria do design.
Por fim, se um membro da equipe tiver mais tempo disponível, poderá corrigir um dos problemas do "futuro" e, se conseguir, migrá-lo para "regular" (enquanto atualiza o status do rastreador de problemas).
fonte
O problema não está falhando nos testes, é um indicador de regressão simples e sem contexto. Aka: como desenvolvedor, posso verificar um único indicador e saber se introduzi um código de regressão ou quebra.
No momento em que você introduzir o conceito de falhas "leves" (tudo bem, estamos trabalhando nisso / ainda não implementado / aguardando a nova versão / ela passará novamente assim que a outra compilação for corrigida), você precisará todo mundo que pode executar ou examinar o teste para saber o estado esperado. O que em uma equipe grande o suficiente mudará a cada hora: seu indicador se torna sem sentido. Em um contexto menor (teste de integração privada da equipe, por exemplo), acho que é como uma dívida técnica e está bem - só precisa ser gerenciado.
A maneira como a maioria das ferramentas aborda isso é 'verde / passante' reflete qual é o resultado esperado - não que o código esteja funcionando:
Aqueles vêm com seus próprios problemas (como você distingue entre 'sim, sabemos que está quebrado, trabalhando nisso' e 'o comportamento correto é uma exceção') - mas eles ajudam.
fonte
Eu uso testes ignorados.
Na estrutura de teste de unidade específica que eu uso, posso gerar uma exceção SkipTest. O teste não é realmente executado e sua falha não interrompe a compilação. No entanto, posso ver o número de testes ignorados e ver se há trabalho a ser feito nessa área.
fonte