Qual é o sentido de executar testes de unidade em um servidor de IC?


98

Por que você executaria testes de unidade em um servidor de IC?

Certamente, quando algo é comprometido em dominar, um desenvolvedor já executou todos os testes de unidade anteriores e corrigiu os erros que poderiam ter ocorrido com seu novo código. Não é esse o objetivo dos testes de unidade? Caso contrário, eles apenas confirmaram o código quebrado.


51
Nossos desenvolvedores não podem se comprometer a dominar. Eles enviam para uma ramificação de recursos, o servidor de IC mescla-se ao mestre e executa testes. Se tiverem sucesso, então as alterações são mescladas de dominar. Portanto, o código com testes quebrados não pode estar no mestre ...
Boris the Spider

2
@BoristheSpider - muito bom fluxo de trabalho. mastersempre deve ser sensato e, de preferência, implantado automaticamente em cada mesclagem em um ambiente de teste para controle de qualidade interno e testes.
Per Lundberg

130
"Certamente, quando algo é comprometido em dominar, um desenvolvedor já executou todos os testes de unidade anteriores e corrigiu os erros que poderiam ter ocorrido com seu novo código". Em que mundo de fantasia você mora?
Jpmc26

5
Em alguns setores, a parte importante não é apenas executar os testes no código, é executar os testes nos binários . A execução dos testes na saída do IC significa que você pode garantir que o produto entregue funcione, porque o binário exato que seu cliente recebeu foi o que passou em todos os seus testes. Parece trivial, mas às vezes isso pode ter um efeito (um que eu já vi é ofuscação; em projetos complexos, ou quando configurado de forma estranha, pode causar problemas na compilação ofuscada que não existiam na versão limpa).
Anaximander 28/01

5
"Certamente, quando algo é comprometido em dominar, um desenvolvedor já executou todos os testes de unidade antes e corrigiu os erros que poderiam ter ocorrido com seu novo código." ... não tenho certeza se é sério
chucksmash

Respostas:


223

Certamente, quando algo é comprometido em dominar, um desenvolvedor já executou todos os testes de unidade anteriores e corrigiu os erros que poderiam ter ocorrido com seu novo código.

Ou não. Pode haver muitas razões pelas quais isso pode acontecer:

  • O desenvolvedor não tem disciplina para fazer isso
  • Eles esqueceram
  • Eles não confirmaram tudo e enviaram um conjunto de confirmação incompleto (obrigado Matthieu M.
  • Eles só executaram alguns testes, mas não o conjunto inteiro (obrigado nhgrif )
  • Eles testaram em sua ramificação antes da mesclagem (obrigado nhgrif * 2)

Mas o ponto real é executar os testes em uma máquina que não é a máquina do desenvolvedor. Um que está configurado de maneira diferente.

Isso ajuda a detectar problemas em que os testes e / ou o código dependem de algo específico para uma caixa de desenvolvedor (configuração, dados, fuso horário, localidade, o que for).

Outras boas razões para as compilações de IC executar testes:

  • Teste em plataformas diferentes das principais plataformas de desenvolvimento, o que pode ser difícil para um desenvolvedor. (obrigado TZHX )
  • Testes de aceitação / integração / ponta a ponta / execução muito longa podem ser executados no servidor de IC que normalmente não seria executado em uma caixa de desenvolvedor. (obrigado Ixrec )
  • Um desenvolvedor pode fazer uma pequena alteração antes de enviar / confirmar (pensando que essa é uma alteração segura e, portanto, não está executando os testes). (obrigado Ixrec * 2)
  • A configuração do servidor de IC geralmente não inclui todas as ferramentas e configurações do desenvolvedor e, portanto, está mais próxima do sistema de produção
  • Os sistemas de CI criam o projeto a partir do zero, o que significa que as construções são repetíveis
  • Uma alteração na biblioteca pode causar problemas a jusante - um servidor de IC pode ser configurado para criar todas as bases de código dependentes, não apenas a biblioteca

36
Outros motivos comuns: 1) O servidor de IC pode executar testes de alto nível de integração / aceitação que demoram muito para que os desenvolvedores sempre os executem. 2) O desenvolvedor os executou e, em seguida, fez uma pequena alteração antes de pressionar que eles tinham muita certeza de que não quebrariam nada, mas queremos ter certeza.
Ixrec 27/01

11
Uma mudança em uma dependência também costuma executar também todas as versões posteriores. Se uma alteração feita por um desenvolvedor interrompe algo a jusante, isso não é facilmente visto ao modificar uma biblioteca (digamos, alterar um tipo de dados subjacente de um SortedSet para um HashSet (fornecendo apenas o contrato de Set) e alguém a jusante trabalhou na suposição equivocada de que o conjunto foi classificado). Não executar os testes (a jusante) no servidor de IC deixaria esse bug apurar por algum tempo.

2
@MichaelT Boa captura. Isso é realmente a causa de> 90% dos nossos fracassos CI nos dias de hoje, não sei como eu esqueci ...
Ixrec

34
Além disso, executá-los em um ambiente de IC geralmente significa que você configura seu projeto do zero , garantindo que sua compilação seja repetível .
precisa saber é o seguinte

5
Além disso, duas alterações podem ser confirmadas, testadas corretamente separadamente, mas são separadas (por exemplo, uma removendo uma API não utilizada e a outra começando a usá-la).
Simon Richter

74

Como desenvolvedor que não executa todos os testes de integração e unidade antes de confirmar o controle de origem, oferecerei minha defesa aqui.

Eu precisaria criar, testar e verificar se um aplicativo é executado corretamente em:

  • Microsoft Windows XP e Vista com o compilador Visual Studio 2008.
  • Microsoft Windows 7 com o compilador do Visual Studio 2010.
    • Ah, e o MSI cria para cada um deles.
  • RHEL 5 e 6 com 4.1 e 4.4 respectivamente (similarmente ao CentOS)
    • 7 em breve. Woop-de-woop.
  • Estação de trabalho Fedora com GCC para as três últimas versões recentes.
  • Debian (e derivados como o Ubuntu) para as últimas três versões recentes.
  • Mac OSX nas últimas três versões recentes.
    • E os pacotes (rpm, dmg, etc)

Adicione os componentes Fortran (com compiladores Intel e GNU), Python (e suas várias versões, dependendo do sistema operacional) e scripts de bash / bat e, bem, acho que você pode ver as coisas em espiral

Então, são dezesseis máquinas que eu teria que ter, apenas para executar alguns testes algumas vezes por dia. Seria quase um trabalho em tempo integral apenas para gerenciar a infraestrutura para isso. Acho que quase todo mundo concorda que isso não é razoável, especialmente multiplicando-o pelo número de pessoas no projeto. Então, deixamos nossos servidores de IC fazerem o trabalho.

Os testes de unidade não impedem que você cometa código quebrado, eles dizem se eles sabem que você quebrou algo. As pessoas podem dizer que "os testes de unidade devem ser rápidos" e continuar com os princípios, padrões e metodologias de projeto, mas, na realidade, às vezes é melhor deixar os computadores que projetamos para tarefas repetitivas e monótonas executá-los e se envolver apenas se eles diga-nos que encontraram algo.


3
O teste de unidade testa o código, não as configurações. Seria gravemente inerte de que você adicione um novo teste e jogá-lo por cima do muro, mesmo sem executá-lo localmente em primeiro lugar ...
Robbie Dee

33
@RobbieDee Receio não entender seu ponto de vista? Eu não sugiro a criação de novos testes sem testá-los localmente, ou apenas cegamente cometer coisas para controle de origem sem testá-los você mesmo, e eu iria executar os testes na minha própria máquina - mas "configuração" não precisa ser testado para um comportamento consistente , e é melhor fazer isso de forma relativamente rápida quando a mente do desenvolvedor ainda estiver nessa área do que encontrar um problema quando a equipe que usa predominantemente Macs acordar a seis mil quilômetros de distância e atualizar suas cópias.
TZHX 27/01

7
@RobbieDee, eu diria que o TZHX executaria todos os testes localmente se eles pudessem fazê-lo, mas não podem . Como o TZHX não pode, eles executam alguns testes localmente (aqueles que podem ser executados no sistema de desenvolvimento e são curtos o suficiente ou mais relevantes para o código alterado, por exemplo) e deixam a bateria cheia funcionar no sistema de CI. Bastante razoável.
muru 27/01

11
@RobbieDee: Ele acredita em testes de unidade. Então ele os testa no ar do Macbook, passa e faz check-in. Os servidores de CI executando o Red Hat, Solaris e Windows executam esses testes novamente. Não é legal saber que o que você testou também funciona em plataformas de produção?
slebetman

2
@RobbieDee: Costumo escrever Testes de Unidade específicos de um determinado compilador em uma determinada plataforma. Considere, por exemplo, um subsistema gráfico que utilize instruções de CPU específicas da AMD (concorrente da Intel), disponíveis apenas na versão 4.5 ou mais recente do g ++ (compilador GNU C ++) 4.5, mas por acaso trabalhei em uma CPU Atom e ICC (a Intel C ++ Compilador). Seria um absurdo executar os testes AMD / g ++ 4.5 todas as vezes nessa máquina, mas é um código a ser testado antes do lançamento; além disso, meu próprio código independente da CPU deve ser testado para garantir a interoperabilidade adequada. Claro, existem VMs e emuladores, ...
phresnel

23

Além da excelente resposta Oded:

  • Você testa o código do repositório . Pode funcionar em sua máquina com seus arquivos ... que você esqueceu de confirmar. Pode depender de uma nova tabela que não possua o script de criação (In liquibase, por exemplo), alguns dados de configuração ou arquivos de propriedades.
  • Você evita problemas de integração de código. Um desenvolvedor baixa a última versão, cria teste de unidade e integração, adiciona código, passa em todos os testes em sua máquina, confirma e pressiona. Outro desenvolvedor acabou de fazer o mesmo. Ambas as alterações são corretas por si só, mas quando mescladas causam um bug. Pode ser a mesclagem do repositório ou apenas que ele não é detectado como um conflito. Por exemplo, o Dev 1 exclui o arquivo que não foi usado. O Dev 2 codifica esse arquivo e testa sem as alterações do Dev 1.
  • Você desenvolve um script para implantar automaticamente a partir do repositório. Ter um script de construção e implantação universal resolve muitos problemas. Alguns desenvolvedores podem ter adicionado uma opção de compilação ou lib que não é compartilhada por todos. Isso não apenas economiza seu tempo, como também torna a implantação segura e previsível. Além disso, você pode voltar no seu repositório para a versão 2.3.1 e implantar esta versão com um script que funcione com esta versão. Inclui objetos de banco de dados, como visualizações, procedimentos armazenados, visualizações e gatilhos que devem ser versionados. (Ou você não poderá voltar para uma versão viável).
  • Outros testes : como integração, desempenho e testes de ponta a ponta. Isso pode ser lento e pode incluir ferramentas de teste como o Selenium. Você pode precisar de um conjunto completo de dados com um banco de dados real em vez de objetos simulados ou HSQL.

Certa vez, trabalhei em uma empresa que apresentava muitos bugs na implantação devido ao processo de fusão e implantação. Isso foi causado por uma estrutura proprietária estranha que dificultou o teste e o IC. Não foi uma experiência feliz descobrir que o código que funcionava perfeitamente no desenvolvimento não chegava diretamente à produção.


Sim, simplesmente esquecer de confirmar algumas das mudanças é muito comum. Eu diria que esquecer de "svn add" novos arquivos e esquecê-los mais tarde é a maneira mais popular de obter uma compilação automática com falha.
Sharptooth

22

Você pensaria que não, mas os desenvolvedores são humanos e às vezes esquecem.

Além disso, os desenvolvedores geralmente falham em obter o código mais recente. Seus testes mais recentes podem funcionar bem e, no momento do check-in, alguém comete uma alteração de última hora.

Seus testes também podem contar com um recurso local (não verificado). Algo que seus testes de unidade local não pegariam.

Se você acha que tudo isso é fantástico, existe um nível acima do IC (pelo menos no TFS) chamado Gated, em que as construções que têm testes falhos são arquivadas e não são comprometidas com a base de código.


7
Eu já vi mais ops que eu esqueci de cometer as falhas de IC que eu gostaria de admitir.
Dan Neely 27/01

@DanNeely Para ser justo, ele bate a obtenção seu traseiro chutado pelo gerente de construção porque você se esqueceu de dizer a ele / ela sobre algo ... :-)
Robbie Dee

3
Essa é uma das razões pelas quais eu amo a CI. Descobrir e consertar suas próprias ooopses é muito melhor do que alguém encontrá-las para você.
Dan Neely 27/01

14

no momento em que algo se compromete a dominar

Normalmente, configurei meu IC para executar em todas as confirmações. As ramificações não são mescladas no master até que a ramificação tenha sido testada. Se você está confiando na execução de testes no master, isso abre uma janela para a compilação ser quebrada.

A execução dos testes em uma máquina de IC é sobre resultados reproduzíveis. Como o servidor de IC possui um ambiente limpo conhecido extraído do seu VCS, você sabe que os resultados do teste estão corretos. Ao executar localmente, você pode esquecer de confirmar algum código necessário para que eles passem ou ter um código não confirmado que os faz passar quando deveriam estar falhando.

Ele também pode economizar tempo dos desenvolvedores executando conjuntos diferentes em paralelo, especialmente se alguns forem testes lentos e de vários minutos que provavelmente não serão executados localmente após cada alteração.

No meu trabalho atual, nossa implantação de produção depende do IC aprovado em todos os testes. Os scripts de implantação impedirão a implantação, a menos que estejam passando. Isso torna impossível esquecer acidentalmente de executá-los.

O IC fazendo parte do fluxo de trabalho também sobrecarrega os desenvolvedores. Como desenvolvedor, você costuma executar um teste de linter, analisador estático, teste de unidade, cobertura de código e integração para cada alteração? O CI pode, de forma totalmente automática e sem a necessidade de pensar nisso - reduzindo o cansaço da decisão.


1
Você realmente não deve ter testes de unidade lentos - isso viola os PRIMEIROS princípios.
Robbie Dee

4
@RobbieDee: Eu acho que geralmente o servidor de CI executa todos os testes, não apenas os testes de unidade.
RemcoGerlich 27/01

4
@RobbieDee: em teoria, todos os testes de unidade são rápidos. Na prática ... Independentemente disso, o IC pode e deve executar todos os testes - linters, análise estática, testes de unidade, testes de integração.
Daenyth 27/01

2
@RobbieDee Obviamente, as especificidades da configuração variam de equipe para equipe. Mesmo quando as construções demoram vários minutos, geralmente é possível executar várias dessas construções em paralelo. Dada uma única base de código monolítica, pode ser uma desvantagem maior, mas o IME não é uma barreira.
Daenyth 27/01

1
@RobbieDee Acho que depende mais da sua arquitetura. Eu já vi isso funcionar à mão para uma equipe de engenharia de aproximadamente 80 anos, mas isso inclui sub-equipes bem definidas para áreas de produtos.
Daenyth 28/01

4

Quando algo se compromete a dominar, um desenvolvedor já deve ter executado todos os testes de unidade ... mas e se não o fizerem? Se você não executar os testes de unidade no servidor de IC, não saberá até que outra pessoa faça as alterações na máquina e descubra os testes que acabaram de ser executados.

Além disso, o desenvolvedor pode ter cometido um erro e referenciado um recurso local específico para sua máquina. Quando eles fazem o check-in do código e a execução do IC falha, o problema é imediatamente identificado e pode ser corrigido.


3

Supondo (ao contrário de outras respostas) que os desenvolvedores são bastante disciplinados e executam testes de unidade antes de confirmar, pode haver vários motivos:

  • testes de unidade em execução podem demorar muito para uma configuração especial. Por exemplo, a execução de testes de unidade com o verificador de memória (como o valgrind) pode levar muito mais tempo. Embora todos os testes de unidade estejam passando, a verificação da memória pode falhar.
  • o resultado não é tão importante para algumas configurações especiais - por exemplo, executar testes de unidade para verificar a cobertura do código requer sinalizadores de compilação especiais. Para desenvolvedores normais, a cobertura do código não é tão importante - é mais para as pessoas cuidando que o código mantenha uma certa qualidade, como os líderes da equipe.

3

É possível imaginar casos em que a alteração A não interrompe o teste e a alteração B não interrompe o teste, mas A e B juntos o fazem. Se A e B forem criados por diferentes desenvolvedores, apenas o servidor de CI detectará o novo bug. A e B podem até ser duas partes da mesma sentença mais longa.

Imagine um trem dirigido pelas duas locomotivas A e B. Talvez uma seja mais que suficiente e essa é a solução a ser aplicada. No entanto, se as duas "correções" forem aplicadas removendo as duas, o trem não se moverá.

Além disso, nem todos os desenvolvedores executam todos os testes de unidade, enquanto a maioria dos bons desenvolvedores faz.


2

Vamos fazer uma pergunta equivalente:

Por que você criaria o código em um servidor de IC?

Certamente, quando algo é comprometido em dominar, um desenvolvedor já criou o código antes e corrigiu os erros que poderiam ter ocorrido com o novo código. Não é esse o objetivo da construção de código? Caso contrário, eles apenas confirmaram o código quebrado.


Existem várias razões para executar o IC, mas o principal ponto do IC é ter uma idéia de qual é o estado do código ao longo do tempo. O principal benefício (dentre vários) que isso fornece é que podemos descobrir quando a compilação é interrompida, descobrir o que a quebrou e depois corrigi-la.

Se o código nunca é quebrado, por que usamos o CI? Para entregar compilações para teste, as compilações noturnas seriam boas o suficiente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.