Acelerando os testes RSpec em um grande aplicativo Rails

90

Eu tenho um aplicativo Rails com mais de 2.000 exemplos em meus testes RSpec. Não é preciso dizer que é um aplicativo grande e que há muito a ser testado. Executar esses testes neste ponto é muito ineficiente e, como leva muito tempo, estamos quase no ponto de ser desencorajados de escrevê-los antes de enviar uma nova compilação. Eu adicionei --profile ao meu spec.opts para encontrar os exemplos de execução mais longa e há pelo menos 10 deles que levam em média 10 segundos para serem executados. Isso é normal entre vocês, especialistas em RSpec? 10 segundos são muito longos para um exemplo? Eu percebo que com 2.000 exemplos, levará uma quantidade não trivial de tempo para testar tudo completamente - mas neste ponto 4 horas é um pouco ridículo.

Que tipo de vezes você está vendo em seus exemplos mais antigos? O que posso fazer para solucionar minhas especificações existentes, a fim de descobrir gargalos e ajudar a acelerar as coisas. Cada minuto realmente ajudaria neste ponto.

randombits
fonte
1
Os testes lentos são testes de integração? Eles estão atingindo um banco de dados? Se sim, com que frequência o banco de dados está sendo recarregado e você pode simular o banco de dados?
Kaleb Pederson
1
Você é capaz de apenas executar parte das especificações que são relevantes para a parte em que está trabalhando, semelhante ao autoteste do SeattleRB? Você tem um servidor de integração contínua que pode executar todos os testes?
Andrew Grimm
Lembre-se também de que todas as coisas são relativas. Eu ouvi "grrr, nosso conjunto de testes leva uma eternidade" tanto para 20 minutos ... quanto para 16-20 horas. Está tudo nos olhos de quem vê. 10 segundos para um determinado teste geralmente significa um teste de unidade que se tornou um teste de integração, conforme mencionado abaixo.
Michael Durrant
Uma sugestão para esse tipo de problema: use perftools.rbjunto com sua estrutura de teste para entender o que está consumindo a maior parte do seu tempo. Atenda às 10 chamadas principais e tente eliminá-las / ignorá-las. Em seguida, repita, até ficar feliz.
aledalgrande

Respostas:

118

10 segundos é um tempo muito longo para a execução de um único teste. Meu pressentimento é que seu alvo de especificações está executando testes de unidade e integração ao mesmo tempo. Isso é uma coisa típica em que os projetos se enquadram e, em algum momento, você precisará superar essa dívida técnica se quiser produzir mais e mais rápido. Existem várias estratégias que podem ajudá-lo a fazer isso ... e vou recomendar algumas que usei no passado.

1. Separe a unidade dos testes de integração

A primeira coisa que eu faria é separar a unidade dos testes de integração. Você pode fazer isso:

  1. Movendo-os (em pastas separadas no diretório de especificações) - e modificando os alvos de rake
  2. Marcando-os (rspec permite marcar seus testes)

A filosofia é que você deseja que suas compilações regulares sejam rápidas - caso contrário, as pessoas não ficarão muito felizes em executá-las com frequência. Então volte para aquele território. Faça com que seus testes regulares sejam executados rapidamente e use um servidor de integração contínua para executar a construção mais completa.

Um teste de integração é um teste que envolve dependências externas (por exemplo, Database, WebService, Queue e alguns diriam FileSystem). Um teste de unidade apenas testa o item específico do código que você deseja verificar. Deve ser executado rápido (9000 em 45 segundos é possível), ou seja, a maior parte deve ser executado na memória.

2. Converter testes de integração em testes de unidade

Se a maior parte de seus testes de unidade for menor do que seu conjunto de testes de integração, você tem um problema. O que isso significa é que as inconsistências começarão a aparecer com mais facilidade. Então, a partir daqui, comece a criar mais testes de unidade para substituir os testes de integração. As coisas que você pode fazer para ajudar neste processo são:

  1. Use uma estrutura de simulação em vez do recurso real. Rspec tem uma estrutura de mocking embutida.
  2. Execute rcov em seu conjunto de teste de unidade. Use isso para avaliar o quão completo é o seu conjunto de testes de unidade.

Assim que tiver um (s) teste (s) de unidade adequado (s) para substituir um teste de integração - remova o teste de integração. O teste duplicado só piora a manutenção.

3. Não use luminárias

As luminárias são más. Em vez disso, use uma fábrica (mecânico ou robô de fábrica). Esses sistemas podem construir gráficos de dados mais adaptáveis ​​e, mais importante, eles podem construir objetos na memória que você pode usar, em vez de carregar coisas de uma fonte de dados externa.

4. Adicionar verificações para interromper os testes de unidade que se tornam testes de integração

Agora que você implementou testes mais rápidos, é hora de colocar verificações para PARAR que isso ocorra novamente.

Existem bibliotecas que fazem o monkey patch active record para lançar um erro ao tentar acessar o banco de dados (UnitRecord).

Você também pode tentar emparelhar e TDD, o que pode ajudar a forçar sua equipe a escrever testes mais rápidos porque:

  1. Alguém está verificando - então ninguém fica preguiçoso
  2. O TDD adequado requer feedback rápido. Testes lentos apenas tornam tudo doloroso.

5. Use outras bibliotecas para superar o problema

Alguém mencionou spork (acelera os tempos de carregamento para o conjunto de testes em rails3), hydra / parallel_tests - para executar testes de unidade em paralelo (em vários núcleos).

Isso provavelmente deve ser usado POR ÚLTIMO. Seu verdadeiro problema está na etapa 1, 2, 3. Resolva isso e você estará em uma posição melhor para desenvolver a infraestrutura adicional.

Anchova
fonte
Ótima resposta. É bem verdade que nenhuma ferramenta avançada pode ajudar a executar testes ruins rapidamente. Portanto, tente escrever apenas bons.
Yura Omelchuk
5
Discordo em seu terceiro ponto de que você não deve usar luminárias. Se usados ​​corretamente, eles são mais rápidos do que fábricas, pois você não precisa criar objetos. Seus dados estão lá, você não precisa criá-los com cada caso de teste.
wallerjake de
3
Isso sobre as fábricas serem mais rápidas é uma piada, certo?
Mike Szyndel
Acelere os testes evitando seletivamente a Factory Girl - robots.thoughtbot.com/…
geekdev
16

Para obter um ótimo livro de receitas sobre como melhorar o desempenho de sua suíte de testes, confira a apresentação Grease Your Suite .

Ele documenta uma aceleração de 45 vezes no tempo de execução do conjunto de testes, utilizando técnicas como:

marechally
fonte
Gosto dos links, mas não da apresentação. Os cartões de índice para o discurso de alguém são uma sugestão para o orador preencher a carne da apresentação.
Brian Maltzan,
Esta apresentação não está mais no ar. fast_context acelera vários blocos de deveria. Quickerclip (esse link também está morto) aparentemente acelera o Paperclip. Hydra está obsoleto para parallel_tests e Gorgon. O bootsnap tem ajustes no sistema de arquivos e agora está incluído no Rails. Versões recentes de Ruby melhoraram a alocação e GC.
representante de
5

Você pode usar o Spork. Tem suporte para 2.3.x,

https://github.com/sporkrb/spork

ou ./script/spec_server que pode funcionar para 2.x

Além disso, você pode editar a configuração do banco de dados (o que essencialmente acelera as consultas ao banco de dados, etc.), o que também aumentará o desempenho dos testes.

Rishav Rastogi
fonte
Spork é incrível. Também funciona no Rails 3, com um pouco de hacking. Mas uma coisa a se notar com o Spork on Rails 3 é que ele não parece captar mudanças nos controladores, então você precisará reiniciá-lo de vez em quando. Mas de qualquer forma, o Spork é realmente incrível, a melhora na velocidade dos testes é muito significativa.
Sobre Ruby,
4
O Spork serve apenas para acelerar a inicialização, não para testes. Portanto, com muitas especificações, a vantagem é insignificante.
Reactormonk,
Ao contrário dos comentários acima, o spork pode ser usado para acelerar os testes, bem como a inicialização de acordo com railscast railscasts.com/episodes/285-spork . Melhorei muito meu conjunto de testes rspec em um aplicativo muito grande. (Reduzido de 192 segundos para 10 segundos!)
jamesc
3

10 segundos por exemplo parece muito tempo. Nunca vi uma especificação que demorasse mais de um segundo e a maioria demorasse muito menos. Você está testando as conexões de rede? Gravações de banco de dados? Sistema de arquivos escreve?

Use mocks e stubs tanto quanto possível - eles são muito mais rápidos do que escrever código que chega ao banco de dados. Infelizmente, mocking e stubbing também levam mais tempo para escrever (e são mais difíceis de fazer corretamente). Você deve equilibrar o tempo gasto escrevendo testes e o tempo gasto executando testes.

Eu segundo Andrew Grimm comentário é sobre olhar para um sistema de CI que pode permitir que você paralelizar sua suíte de testes. Para algo desse tamanho, pode ser a única solução viável.

zetético
fonte
sim, se fosse 10 segundos, eu faria o perfil e ver onde está o gargalo
rogerdpack,
1
Tenho um projeto enorme e a execução de um único teste rspec leva cerca de 15-20 segundos.
ExiRe
2

Várias pessoas mencionaram Hydra acima. Nós o usamos com grande sucesso no passado. Recentemente documentei o processo de colocar a Hydra em funcionamento: http://logicalfriday.com/2011/05/18/faster-rails-tests-with-hydra/

Eu concordaria com o sentimento de que esse tipo de técnica não deve ser usado como um substituto para escrever testes que são bem estruturados e rápidos por padrão.

Rodreegez
fonte
0

Você pode seguir algumas dicas simples para primeiro investigar onde é gasto a maior parte do tempo, caso ainda não tenha experimentado. Veja o artigo abaixo:

https://blog.mavenhive.in/7-tips-to-speed-up-your-webdriver-tests-4f4d043ad581

Acho que a maioria dessas etapas são genéricas que também se aplicam independentemente da ferramenta usada para o teste.

jake
fonte
-4

Exclua o conjunto de testes existente. Será incrivelmente eficaz.

thedanotto
fonte