Accélération des tests RSpec dans une grande application Rails


90

J'ai une application Rails avec plus de 2000 exemples dans mes tests RSpec. Inutile de dire que c'est une application volumineuse et qu'il y a beaucoup à tester. Exécuter ces tests à ce stade est très inefficace et parce que cela prend tellement de temps, nous sommes presque sur le point d'être découragés de les écrire avant de pousser une nouvelle version. J'ai ajouté --profile à mes spécifications.opts pour trouver les exemples les plus longs et il y en a au moins 10 qui prennent en moyenne 10 secondes à s'exécuter. Est-ce normal parmi vous les experts RSpec? Est-ce que 10 secondes sont trop longues pour un exemple? Je me rends compte qu'avec 2000 exemples, il faudra un temps non négligeable pour tout tester à fond - mais à ce stade, 4 heures, c'est un peu ridicule.

Quel genre de temps voyez-vous pour vos exemples les plus anciens? Que puis-je faire pour dépanner mes spécifications existantes afin de déterminer les goulots d'étranglement et d'accélérer les choses. Chaque minute aiderait vraiment à ce stade.


1
Les tests d'intégration des tests lents sont-ils des tests? Frappent-ils un DB? Si tel est le cas, à quelle fréquence la base de données est-elle rechargée et pouvez-vous vous moquer de la base de données?
Kaleb Pederson

1
Pouvez-vous simplement exécuter une partie des spécifications qui sont pertinentes pour la partie sur laquelle vous travaillez, comme l'autotest de SeattleRB? Avez-vous un serveur d'intégration continue qui peut exécuter tous les tests?
Andrew Grimm

Souvenez-vous aussi que tout est relatif. J'ai entendu "grrr, notre suite de tests prend une éternité" pendant 20 minutes ... et 16-20 heures. Tout est dans l'œil du spectateur. 10 secondes pour un test donné signifie souvent un test unitaire qui est devenu un test d'intégration comme mentionné ci-dessous.
Michael Durrant

Une suggestion pour ce genre de problème: utilisez-le perftools.rbavec votre framework de test pour comprendre ce qui vous consomme le plus de temps. Prenez les 10 meilleurs appels et essayez de les éliminer / les parcourir. Puis répétez, jusqu'à ce que vous soyez heureux.
aledalgrande

Réponses:


118

10 secondes, c'est un temps très long pour un seul test à exécuter. Mon instinct est que votre cible de spécifications exécute simultanément des tests unitaires et d'intégration. C'est une chose typique dans laquelle les projets tombent et à un certain stade, vous devrez surmonter cette dette technique si vous voulez produire plus, plus rapidement. Il existe un certain nombre de stratégies qui peuvent vous aider à le faire ... et je vais en recommander quelques-unes que j'ai utilisées dans le passé.

1. Séparer l'unité des tests d'intégration

La première chose que je ferais est de séparer l'unité des tests d'intégration. Vous pouvez le faire soit en:

  1. Les déplacer (dans des dossiers séparés sous le répertoire spec) - et modifier les cibles de rake
  2. Les baliser (rspec vous permet de baliser vos tests)

La philosophie est que vous voulez que vos builds réguliers soient rapides - sinon les gens ne seront pas trop heureux de les exécuter souvent. Revenez donc sur ce territoire. Exécutez rapidement vos tests réguliers et utilisez un serveur d'intégration continue pour exécuter la version la plus complète.

Un test d'intégration est un test qui implique des dépendances externes (par exemple, base de données, WebService, file d'attente et certains diraient FileSystem). Un test unitaire teste simplement l'élément de code spécifique que vous souhaitez vérifier. Il devrait fonctionner rapidement (9000 en 45 secondes est possible), c'est-à-dire que la plupart devrait fonctionner en mémoire.

2. Convertir les tests d'intégration en tests unitaires

Si la majeure partie de vos tests unitaires est inférieure à votre suite de tests d'intégration, vous avez un problème. Cela signifie que les incohérences commenceront à apparaître plus facilement. À partir de là, commencez à créer plus de tests unitaires pour remplacer les tests d'intégration. Les choses que vous pouvez faire pour aider dans ce processus sont:

  1. Utilisez un cadre moqueur au lieu de la vraie ressource. Rspec a un cadre de simulation intégré.
  2. Exécutez rcov sur votre suite de tests unitaires. Utilisez-le pour évaluer la profondeur de votre suite de tests unitaires.

Une fois que vous avez un ou plusieurs tests unitaires appropriés pour remplacer un test d'intégration, supprimez le test d'intégration. Les tests en double ne font qu'empirer la maintenance.

3. N'utilisez pas d'appareils

Les luminaires sont mauvais. Utilisez plutôt une usine (machiniste ou factorybot). Ces systèmes peuvent créer des graphiques de données plus adaptables et, plus important encore, ils peuvent créer des objets en mémoire que vous pouvez utiliser, plutôt que de charger des éléments à partir d'une source de données externe.

4. Ajouter des vérifications pour empêcher les tests unitaires de devenir des tests d'intégration

Maintenant que vous avez mis en place des tests plus rapides, il est temps de vérifier pour empêcher que cela ne se reproduise.

Il existe des bibliothèques dont l'enregistrement actif de monkey patch génère une erreur lors de la tentative d'accès à la base de données (UnitRecord).

Vous pouvez également essayer le couplage et le TDD, ce qui peut aider votre équipe à écrire des tests plus rapides car:

  1. Quelqu'un vérifie - donc personne ne devient paresseux
  2. Un TDD approprié nécessite une rétroaction rapide. Des tests lents rendent tout cela douloureux.

5. Utilisez d'autres bibliothèques pour résoudre le problème

Quelqu'un a mentionné spork (accélère les temps de chargement de la suite de tests sous rails3), hydra / parallel_tests - pour exécuter des tests unitaires en parallèle (sur plusieurs cœurs).

Cela devrait probablement être utilisé en dernier. Votre véritable problème se situe tout au long des étapes 1, 2 et 3. Résolvez-le et vous serez mieux placé pour attribuer une infrastructure supplémentaire.


Très bonne réponse. Il est très vrai qu'aucun outil avancé ne peut aider à exécuter rapidement de mauvais tests. Alors essayez d'écrire seulement de bons.
Yura Omelchuk

5
Je ne suis pas d'accord sur votre troisième point selon lequel vous ne devriez pas utiliser de luminaires. S'ils sont utilisés correctement, ils sont plus rapides que les usines car vous n'avez pas à créer d'objets. Vos données sont là, vous n'avez pas à les créer avec chaque scénario de test.
wallerjake

3
Cela à propos des usines étant plus rapides est une blague, non?
Mike Szyndel

Accélérez les tests en évitant sélectivement Factory Girl - robots.thoughtbot.com
...

16

Pour un excellent livre de recettes sur l'amélioration des performances de votre suite de tests, consultez la présentation Grease Your Suite .

Il documente une accélération de 45x dans le temps d'exécution de la suite de tests en utilisant des techniques telles que:


J'aime les liens, mais pas la présentation. Les fiches pour le discours de quelqu'un invitent l'orateur à remplir le contenu de la présentation.
Brian Maltzan

Cette présentation n'est plus en place. fast_context accélère plusieurs blocs shoulda. quickerclip (ce lien également mort) accélère apparemment Paperclip. Hydra est obsolète pour parallel_tests et Gorgon. bootsnap a des modifications du système de fichiers et est maintenant inclus avec Rails. Les versions récentes de Ruby ont amélioré l'allocation et le GC.
rep

5

Vous pouvez utiliser Spork. Il prend en charge 2.3.x,

https://github.com/sporkrb/spork

ou ./script/spec_server qui peut fonctionner pour 2.x

Vous pouvez également modifier la configuration de la base de données (ce qui accélère essentiellement les requêtes de base de données, etc.), ce qui augmentera également les performances des tests.


Spork est génial. Cela fonctionne également sur Rails 3, avec un peu de piratage. Mais une chose à noter avec Spork on Rails 3 est qu'il ne semble pas détecter les changements dans les contrôleurs, vous devrez donc le redémarrer de temps en temps. Mais de toute façon, Spork est vraiment génial, l'amélioration de la vitesse des tests est très significative.
AboutRuby

4
Spork sert uniquement à accélérer le démarrage, pas les tests. Donc, avec beaucoup de spécifications, l'avantage est insignifiant.
Reactormonk

Contrairement aux commentaires ci-dessus, spork peut être utilisé pour accélérer les tests ainsi que le démarrage selon railscast railscasts.com/episodes/285-spork . Amélioration massive de ma suite de tests rspec sur une très grande application. (Réduit de 192 secondes à 10 secondes!)
JamesC

3

10 secondes par exemple semblent très longues. Je n'ai jamais vu une spécification qui prenait plus d'une seconde, et la plupart en prennent beaucoup moins. Testez-vous les connexions réseau? La base de données écrit? Le système de fichiers écrit?

Utilisez autant que possible des simulations et des stubs - ils sont beaucoup plus rapides que l'écriture de code qui atteint la base de données. Malheureusement, les moqueries et les stubbing prennent également plus de temps à écrire (et sont plus difficiles à faire correctement). Vous devez équilibrer le temps passé à écrire les tests par rapport au temps passé à exécuter les tests.

J'appuie le commentaire d' Andrew Grimm sur la recherche d'un système CI qui pourrait vous permettre de paralléliser votre suite de tests. Pour quelque chose de cette taille, cela pourrait être la seule solution viable.


oui si c'est 10 secondes, je le profilerais et verrais où se trouve le goulot d'étranglement
rogerdpack

1
J'ai un énorme projet et exécuter un seul test rspec prend environ 15 à 20 secondes.
Sortie le

2

Plusieurs personnes ont mentionné Hydra ci-dessus. Nous l'avons utilisé avec beaucoup de succès dans le passé. J'ai récemment documenté le processus de mise en service de l'hydra: http://logicalfriday.com/2011/05/18/faster-rails-tests-with-hydra/

Je suis d'accord avec le sentiment que ce type de technique ne devrait pas être utilisé comme un substitut à l'écriture de tests bien structurés et rapides par défaut.





-4

Supprimez la suite de tests existante. Sera incroyablement efficace.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.