Nous saluons le retour! Si vous avez manqué la première partie de notre voyage jusqu'à présent, vous voudrez peut-être y revenir en premier..
Jusqu'à présent, nous avons appliqué un processus de développement piloté par les tests à la construction de notre application, en utilisant le framework de test RSpec. À partir de là, nous allons étudier d'autres fonctionnalités de RSpec et utiliser le joyau Pry pour vous aider à déboguer et à écrire votre code..
Prenons un moment pour passer en revue d'autres fonctionnalités de RSpec dont nous n'avons pas besoin dans cet exemple d'application simple, mais vous pouvez trouver utile de travailler sur votre propre projet..
Imaginez que vous écrivez un test mais que vous soyez interrompu ou que vous deviez partir pour une réunion et que vous n'ayez pas encore terminé le code requis pour réussir le test.
Vous pouvez supprimer le test et le réécrire plus tard lorsque vous pourrez revenir à votre travail. Sinon, vous pouvez simplement commenter le code, mais c'est plutôt moche et certainement nul en utilisant un système de contrôle de version.
La meilleure chose à faire dans cette situation est de définir notre test comme étant "en attente" afin que, chaque fois que les tests sont exécutés, la structure de test ignore le test. Pour ce faire, vous devez utiliser le en attendant
mot-clé:
décrire "une méthode" do it "devrait faire quelque chose" en attendant la fin
Tous les bons frameworks de test vous permettent d'exécuter du code avant et après chaque test. RSpec n'est pas différent.
Il nous fournit avant
et après
des méthodes qui nous permettent de configurer un état spécifique pour l'exécution de notre test, puis de nettoyer cet état une fois le test exécuté (afin que l'état ne fuie pas et n'affecte pas le résultat des tests suivants).
Décrire "une méthode" faire avant (: each) faire # une fin de code de configuration après (: each) faire # une fin de code de suppression "devrait faire quelque chose", fin en attente fin
Nous avons déjà vu le décrire
bloc; mais il y a un autre bloc qui est fonctionnellement équivalent appelé le contexte
. Vous pouvez l'utiliser partout où vous utiliseriez décrire
.
La différence entre eux est subtile mais importante: le contexte
nous permet de définir un état pour notre test. Non explicitement cependant (nous ne définissons pas réellement l'état en définissant un le contexte
block - c'est à des fins de lisibilité, l'intention du code suivant est plus claire).
Voici un exemple:
decrire "Une certaine méthode" le contexte "bloc fourni" "do it" donne le résultat pour bloquer "contexte en attente de fin de fin" aucun bloc fourni "ne le fait" appelle une méthode de repli "ne finit pas en attente de fin
Nous pouvons utiliser le talon
méthode pour créer une fausse version d'un objet existant et lui faire renvoyer une valeur prédéterminée.
Ceci est utile pour empêcher nos tests de toucher les API de service en direct et pour guider nos tests en donnant des résultats prévisibles pour certains appels..
Imaginez que nous ayons une classe appelée La personne
et que cette classe a un parler
méthode. Nous voulons tester cette méthode fonctionne comme nous le prévoyons. Pour ce faire, nous allons stub la parler
méthode en utilisant le code suivant:
décris Personne fais-le "speak ()" do bob = stub () bob.stub (: speak) .and_return ('hello') Person.any_instance.stub (: initialize) .and_return (bob) instance = Person.new expect ( instance.speak) .to eq ('hello') end end
Dans cet exemple, nous disons que "n'importe quelle instance" du La personne
la classe devrait avoir son initialiser
méthode stubbed donc il retourne l'objet bob
.
Vous remarquerez que bob
est lui-même un talon qui est configuré de sorte que tout code temporel tente d'exécuter le parler
méthode il retournera "bonjour".
Nous procédons ensuite pour créer un nouveau La personne
exemple et passer l'appel de instance.speak
dans RSpec attendre
syntaxe.
Nous disons à RSpec que nous nous attendons à ce que l'appel se traduise par la chaîne "hello".
Dans les exemples précédents, nous utilisons la fonctionnalité RSpec et_retour
pour indiquer ce que notre souche devrait revenir quand il s'appelle.
Nous pouvons indiquer une valeur de retour différente chaque fois que le stub est appelé en spécifiant plusieurs arguments à et_retour
méthode:
obj = stub () obj.stub (: foo) .and_return (1, 2, 3) expect (obj.foo ()). à eq (1) expect (obj.foo ()). à eq (2) (obj.foo ()). à eq (3)
Les imitations sont similaires aux Stubs en ce sens que nous créons de fausses versions de nos objets, mais au lieu de renvoyer une valeur prédéfinie, nous guidons plus spécifiquement les itinéraires de nos objets. doit prendre pour que le test soit valide.
Pour ce faire, nous utilisons le moquer
méthode:
décris Obj do it "testing ()" do bob = mock () bob.should_receive (: testing) .with ('content') Obj.any_instance.stub (: initialize) .and_return (bob) instance = Obj.new instance. tester ('une certaine valeur') end end
Dans l'exemple ci-dessus, nous créons un nouveau Objet
par exemple, puis appeler son essai
méthode.
Dans les coulisses de ce code, nous attendons la essai
méthode à appeler avec la valeur 'contenu'
. S'il n'est pas appelé avec cette valeur (ce qui, dans l'exemple ci-dessus, ne l'est pas), nous savons qu'un élément de notre code n'a pas fonctionné correctement..
le assujettir
mot-clé peut être utilisé de différentes manières. Tous sont conçus pour réduire la duplication de code.
Vous pouvez l’utiliser implicitement (notez notre il
le bloc ne fait pas référence assujettir
du tout):
decrire Array decrit "avec 3 items" do subject [1,2,3] it should_not be_empty end end
Vous pouvez l’utiliser explicitement (notez notre il
bloc se réfère à assujettir
directement):
décrivent MyClass décrivent "initialisation" do sujet MyClass il "crée une nouvelle instance" do instance = subject.new expect (instance) .to be_a (MyClass) end end end
Plutôt que de référencer constamment un sujet dans votre code et de transmettre différentes valeurs pour l'instanciation, par exemple:
décrivent "Foo" le contexte "A" do "Bar" do baz = Baz.new ('a') s’attend à (baz.type) .to eq ('a') fin et contexte "B" do it "Bar" do baz = Baz.new ('b') attend (type.baz.) .à eq ('b') fin de contexte "C" do it "Bar" do baz = Baz.new ('c') attend (baz .type) .to eq ('c') fin fin fin
Vous pouvez plutôt utiliser assujettir
de même que laisser
pour réduire les doublons:
décrivent "personne" faire l'objet Personne.nouveau (nom) # personne a un contexte de méthode get_name "Bob" laisser laisser (: nom) 'Bob' son (: get_name) devrait == 'Bob' contexte de fin "Joe" fait laisser (: nom) 'Joe' son (: get_name) devrait == 'Joe' contexte de fin "Smith" fait laisser (: name) 'Smith' son (: get_name) devrait == 'Smith' end end
RSpec propose de nombreuses autres fonctionnalités, mais nous avons examiné les plus importantes que vous utiliserez souvent lors de la rédaction de tests avec RSpec..
Vous pouvez configurer RSpec pour exécuter vos tests dans un ordre aléatoire. Cela vous permet de vous assurer qu'aucun de vos tests ne s'appuie sur les autres tests qui l'entourent..
RSpec.configure do | config | config.order = 'random' end
Vous pouvez définir ceci via la commande en utilisant le --ordre
drapeau / option. Par exemple: rspec - ordre aléatoire
.
Quand vous utilisez le --ordre aléatoire
L'option RSpec affichera le nombre aléatoire utilisé pour ensemencer l'algorithme. Vous pouvez utiliser à nouveau cette valeur de départ lorsque vous pensez avoir découvert un problème de dépendance dans vos tests. Une fois que vous avez résolu le problème, vous pouvez transmettre la valeur de départ à RSpec (par exemple, si la valeur initiale était 1234
puis exécuter --ordre aléatoire: 1234
) et il utilisera la même graine aléatoire pour voir s'il peut répliquer le bogue de dépendance d'origine.
Vous avez vu que nous avons ajouté un ensemble d’objets de configuration spécifiques au projet dans notre Rakefile
. Mais vous pouvez définir les options de configuration globalement en les ajoutant à un .rspec
fichier dans votre répertoire personnel.
Par exemple, à l'intérieur .rspec
:
--couleur - format imbriqué
Nous sommes maintenant prêts à commencer à chercher un moyen de déboguer notre application et notre code de test à l'aide de Pry gem..
Il est important de comprendre que, bien que Pry soit vraiment bon pour déboguer votre code, il est en fait conçu comme un outil amélioré Ruby REPL (pour remplacer irb
) et non strictement à des fins de débogage; Ainsi, par exemple, il n’existe pas de fonctions intégrées telles que: entrer, franchir, quitter, etc. que vous trouverez généralement dans un outil conçu pour le débogage..
Mais comme outil de débogage, Pry est très concentré et maigre.
Nous reviendrons sur le débogage dans un instant, mais examinons d'abord comment nous utiliserons Pry initialement.
Afin de démontrer Pry, je vais ajouter plus de code à mon exemple d'application (ce code supplémentaire n'a aucune incidence sur notre test)
class RSpecGreeter attr_accessor: test @@ class_property = "Je suis une propriété de classe" def greet binding.pry @instance_property = "Je suis une propriété d'instance" pubs privs "Bonjour RSpec!" end def pubs test_var = "Je suis une variable de test" test_var end private def privs met "I'm private" end end
Vous remarquerez que nous avons ajouté des méthodes, des propriétés d'instance et de classe supplémentaires. Nous faisons également appel à deux des nouvelles méthodes que nous avons ajoutées depuis notre saluer
méthode.
Enfin, vous remarquerez l'utilisation de binding.pry
.
binding.pry
Un point d'arrêt est un endroit dans votre code où l'exécution s'interrompt.
Vous pouvez définir plusieurs points d'arrêt dans votre code et les créer à l'aide de binding.pry
.
Lorsque vous exécutez votre code, vous remarquerez que le terminal s’arrêtera et vous placera dans le code de votre application à l’endroit exact où votre binding.pry a été placé..
Voici un exemple de ce à quoi cela pourrait ressembler…
8: def greet => 9: binding.pry 10: pubs 11: privs 12: "Bonjour RSpec!" 13: fin
À partir de ce moment, Pry a accès à la portée locale afin que vous puissiez utiliser Pry comme vous le feriez dans irb
et commencez à taper, par exemple, des variables pour voir quelles valeurs elles tiennent.
Vous pouvez exécuter le sortie
commande pour quitter Pry et pour que votre code continue à s'exécuter.
Où suis-je
Lorsque vous utilisez beaucoup de binding.pry
points de rupture, il peut être difficile de comprendre où, dans l'application, vous êtes.
Pour obtenir un meilleur contexte de votre position à tout moment, vous pouvez utiliser le Où suis-je
commander.
Lorsqu'il est exécuté seul, vous verrez quelque chose de similaire à quand vous avez utilisé binding.pry
(vous verrez la ligne sur laquelle le point d'arrêt a été défini et quelques lignes au-dessus et au-dessous). La différence est que si vous passez un argument numérique supplémentaire comme où 5
vous verrez cinq lignes supplémentaires ci-dessus où le binding.pry
était placé. Vous pouvez demander à voir 100 lignes autour du point de rupture actuel, par exemple.
Cette commande peut vous aider à vous orienter dans le fichier actuel..
wtf
le wtf
commande signifie "what the f ***" et fournit une trace de pile complète pour la dernière exception levée. Cela peut vous aider à comprendre les étapes qui ont conduit à l'erreur survenue.
ls
le ls
commande affiche quelles méthodes et propriétés sont disponibles pour Pry.
Une fois exécuté, il vous montrera quelque chose comme…
RSpecGreeter # méthodes: saluer les pubs test test = variables de classe: @@ locale_classe_propriété: _ __ _dir_ _ex_ _fichier_ _in_ _out_ _pry_
Dans l'exemple ci-dessus, nous pouvons voir que nous avons quatre méthodes publiques (rappelez-vous que nous avons mis à jour notre code pour inclure des méthodes supplémentaires, puis tester
et test =
ont été créés en utilisant Ruby's attr_accessor
main courte).
Il affiche également d'autres variables de classe et locales auxquelles Pry peut accéder..
Une autre chose utile à faire est de rechercher (grp) les résultats uniquement pour ce qui vous intéresse. Vous aurez besoin de comprendre les expressions régulières, mais cela peut être une technique pratique. Voici un exemple…
ls -p -G ^ p => RSpecGreeter # méthodes: privs
Dans l'exemple ci-dessus, nous utilisons le -p
et -g
options / flags qui indiquent à Pry que nous voulons seulement voir les méthodes publiques et privées et que nous utilisons la regex ^ p
(ce qui signifie que tout ce qui commence par p
) comme notre modèle de recherche pour filtrer les résultats.
Fonctionnement ls --help
vous montrera également toutes les options disponibles.
CD
Vous pouvez modifier l'étendue actuelle à l'aide de la touche CD
commander.
Dans notre exemple si nous courons cd… / pubs
ça nous mènera au résultat de cet appel de méthode.
Si nous courons maintenant Où suis-je
vous verrez qu'il affichera Inside "Je suis une variable de test"
.
Si nous courons soi
alors vous verrez que nous obtenons "Je suis une variable de test"
revenu.
Si nous courons auto.classe
nous verrons Chaîne
revenu.
Vous pouvez remonter la chaîne de l’étendue en utilisant CD…
ou vous pouvez revenir au niveau supérieur de la portée en utilisant cd /
.
Note: on pourrait en ajouter un autre binding.pry
à l'intérieur de pubs
méthode, puis notre portée serait à l'intérieur de cette méthode plutôt que le résultat de la méthode.
nidification
Prenons l'exemple précédent de course à pied cd pubs
. Si nous courons le nidification
commande, nous obtiendrons un aperçu du nombre de contextes / niveaux que Pry a actuellement:
Statut d'imbrication: - 0. # (niveau de base du levier) 1. "Je suis une variable de test"
De là nous pouvons courir sortie
pour revenir au contexte précédent (par exemple, à l'intérieur du saluer
méthode)
Fonctionnement sortie
encore une fois, cela signifie que nous fermons le dernier contexte Pry a et donc Pry termine et notre code continue à courir.
méthode de recherche
Si vous ne savez pas où trouver une méthode particulière, vous pouvez utiliser le méthode de recherche
commande pour vous montrer tous les fichiers de votre base de code ayant une méthode qui correspond à ce que vous recherchez:
find-method priv => Kernel Kernel # module privé_methods Module # module privé_instance_méthodes # module privé_constant # module privé_method_defin? Module # private_class_method module # private RSpecGreeter RSpecGreeter # privs
Vous pouvez également utiliser le -c
option / flag pour rechercher le contenu des fichiers à la place:
find-method -c greet => RSpecGreeter RSpecGreeter: def salue RSpecGreeter # privs: salue
suivant
, étape
, continuer
Bien que les techniques ci-dessus soient utiles, il ne s’agit pas vraiment de "débogage" au sens où vous êtes probablement habitué.
Pour la plupart des développeurs, leur éditeur ou navigateur leur fournit un outil de débogage intégré qui leur permet de parcourir leur code code par ligne et de suivre la route empruntée par le code jusqu'à son achèvement..
Pry étant développé pour être utilisé comme REPL, cela ne veut pas dire que ce n’est pas utile pour le débogage..
Une solution naïve consisterait à définir plusieurs binding.pry
déclarations à travers une méthode et l'utilisation ctrl-d pour vous déplacer dans chaque ensemble de points d'arrêt. Mais ce n'est toujours pas assez bon.
Pour un débogage pas à pas, vous pouvez charger le joyau pry-nav…
source "https://rubygems.org" groupe de gem 'rspec': développement ne 'gem' garde 'gem' guard-rspec 'gem' pry '# Ajoute les étapes de débogage à Pry # continue, étape, prochaine gem' pry-remote ' gem 'pry-nav' end
Ce joyau étend Pry pour qu'il comprenne les commandes suivantes:
Suivant
(passer à la ligne suivante)Étape
(passez à la ligne suivante et s'il s'agit d'une méthode, passez à cette méthode)Continuer
(Ignorez tous les points d'arrêt dans ce fichier)En prime, intégrons nos tests au service en ligne CI (intégration continue) Travis-CI.
Le principe de CI est d’engager / pousser tôt et souvent pour éviter les conflits entre votre code et la branche principale. Lorsque vous le faites (dans ce cas, nous nous engageons sur GitHub), vous devriez alors lancer une «construction» sur votre serveur CI, qui exécute les tests appropriés pour vérifier que tout fonctionne correctement..
Maintenant, avec TDD comme principale méthodologie de développement, vous risquez moins de rencontrer des bogues chaque fois que vous appuyez, car vos tests font partie intégrante de votre processus de développement. Par conséquent, avant de lancer ces tests, vous êtes déjà au courant des bogues ou des régressions. Mais cela ne vous protège pas nécessairement contre les bogues générés par les tests d'intégration (tous les codes de plusieurs systèmes sont exécutés ensemble pour garantir le bon fonctionnement du système dans son ensemble)..
Quoi qu'il en soit, le code ne doit jamais être envoyé directement sur votre serveur de production en direct. vous devez toujours le placer d'abord sur un serveur d'infrastructure client pour vous aider à détecter tout bogue potentiel résultant des différences entre votre environnement de développement et l'environnement de production..
Beaucoup de sociétés ont encore beaucoup d'environnements pour que leur code passe avant d'atteindre le serveur de production en direct.
Par exemple, à BBC News, nous avons:
Bien que la configuration de chaque environnement soit identique, le but est d'implémenter différents types de tests afin de garantir le plus grand nombre possible de bogues détectés et résolus avant que le code n'atteigne son statut "en direct"..
Travis CI est un service d'intégration continue hébergé pour la communauté open source. Il est intégré à GitHub et offre un support de première classe pour plusieurs langues.
Cela signifie que Travis-CI offre des services CI gratuits pour les projets open source et propose également un modèle payant aux entreprises et aux organisations qui souhaitent préserver la confidentialité de leur intégration CI..
Nous utiliserons le modèle open-source gratuit sur notre exemple de référentiel GitHub.
Le processus est le suivant:
.travis.yml
fichier dans le répertoire racine de votre projet et validez-le dans votre référentiel GitHubLa dernière étape est la plus importante (créer un .travis.yml
fichier) car cela détermine les paramètres de configuration de Travis-CI pour qu’il sache gérer l’exécution des tests de votre projet.
Jetons un coup d'oeil au .travis.yml
fichier que nous utilisons pour notre exemple de référentiel GitHub:
language: ruby cache: bundler rvm: - 2.0.0 - 1.9.3 script: 'bundle exec rake spec' bundler_args: --ans les branches de développement: uniquement: - notifications principales: email: - [email protected]
Brisons ce morceau par morceau…
Nous spécifions d’abord la langue que nous utilisons dans notre projet. Dans ce cas, nous utilisons Ruby: langue: rubis
.
Parce que lancer Bundler peut être un peu lent et que nous savons que nos dépendances ne vont pas changer, nous pouvons souvent choisir de les mettre en cache, nous avons donc défini cache: bundler
.
Travis-CI utilise RVM (Ruby Version Manager) pour installer Rubies sur leurs serveurs. Nous devons donc spécifier les versions de Ruby sur lesquelles nous voulons exécuter nos tests. Dans ce cas, nous avons choisi 2.0
et 1.9.3
qui sont deux versions populaires de Ruby (techniquement, notre application utilise Ruby 2 mais il est bon de savoir que notre code passe également dans d'autres versions de Ruby):
rvm: - 2.0.0 - 1.9.3
Pour exécuter nos tests, nous savons que nous pouvons utiliser la commande râteau
ou spécification de râteau
. Travis-CI lance par défaut la commande râteau
mais à cause de la manière dont Gems est installé sur Travis-CI en utilisant Bundler, nous devons changer la commande par défaut: script: 'bundle exec rake spec'
. Si nous ne le faisions pas, Travis-CI aurait un problème pour localiser le rspec / core / rake_task
fichier qui est spécifié dans notre Rakefile
.
Remarque: si vous rencontrez des problèmes concernant Travis-CI, vous pouvez rejoindre le canal #travis sur IRC freenode pour obtenir de l'aide pour répondre à vos questions. C’est là que j’ai découvert la solution à mon problème, Travis-CI ne pouvant pas exécuter mes tests avec ses paramètres par défaut. râteau
commande et la suggestion de remplacer la valeur par défaut par bundle exec rake
résolu ce problème.
Ensuite, comme nous ne sommes intéressés que par l'exécution de nos tests, nous pouvons transmettre des arguments supplémentaires à Travis-CI afin de filtrer les gems que nous ne voulons pas déranger l'installation. Donc, pour nous, nous voulons exclure l’installation des gemmes regroupées en tant que développement: bundler_args: --sans développement
(Cela signifie que nous excluons les gemmes qui ne sont réellement utilisées que pour le développement et le débogage, telles que Pry et Guard).
Il est important de noter qu’à l’origine, je chargeais Pry dans notre spec_helper.rb
fichier. Cela posait un problème lors de l’exécution du code sur Travis-CI, maintenant que j’excluais les gemmes «de développement». J'ai donc dû modifier le code comme suit:
nécessite 'pry' si ENV ['APP_ENV'] == 'debug'
Vous pouvez voir que maintenant le joyau Pry est seulement exiger
'ed si une variable d'environnement de APP_ENV
est configuré pour déboguer. De cette façon, nous pouvons éviter que Travis-CI ne génère des erreurs. Cela signifie que lorsque vous exécutez votre code localement, vous devez définir la variable d'environnement si vous souhaitez déboguer votre code à l'aide de Pry. Ce qui suit montre comment cela pourrait être fait sur une seule ligne:
APP_ENV = debug && ruby lib / example.rb
Il y avait deux autres changements que j'ai apportés et qui était à notre Gemfile
. L'une consistait à préciser les types de gemmes requis pour les tests et ceux nécessaires au développement, et l'autre était explicitement requise par Travis-CI:
source groupe "https://rubygems.org": test do gem 'rake' gem 'rspec' fin groupe: développement do gem 'guard' gem 'guard-rspec' gem 'pry' # Ajoute les étapes de débogage à Pry # continue, étape, prochaine gem 'pry-remote' gem 'pry-nav' fin
En regardant les mises à jour ci-dessus Gemfile
nous pouvons voir que nous avons déplacé la gemme RSpec dans un nouveau tester
groupe, alors maintenant il devrait être plus clair quel but chaque gem a. Nous avons également ajouté un nouveau joyau 'rake'
. La documentation de Travis-CI indique que cela doit être spécifié explicitement.
La section suivante est facultative et vous permet de répertorier certaines branches de votre référentiel dans une liste blanche (ou une liste noire). Donc, par défaut, Travis-CI effectuera des tests sur toutes vos branches, sauf indication contraire de votre part. Dans cet exemple, nous disons que nous voulons seulement que cela fonctionne contre notre maîtriser
branche:
branches: seulement: - maître
Nous pourrions lui dire d’exécuter toutes les branches, à l’exception d’une branche particulière, comme suit:
branches: sauf: - some_branch_I_dont_want_run
La dernière section indique à Travis-CI où envoyer les notifications en cas d’échec ou de succès de la construction:
notifications: email: - [email protected]
Vous pouvez spécifier plusieurs adresses électroniques si vous le souhaitez:
notifications: email: - [email protected] - [email protected] - [email protected]
Vous pouvez être plus spécifique et spécifier ce que vous voulez qu'il se passe en cas d'échec ou de succès (par exemple, plus de personnes ne seront intéressées que si les tests échouent au lieu de recevoir un courrier électronique chaque fois qu'ils réussissent):
notifications: email: destinataires: - [email protected] on_failure: changer on_success: jamais
L'exemple ci-dessus montre que le destinataire ne recevra jamais d'e-mail si les tests réussissent, mais sera averti si le statut d'échec change (la valeur par défaut est toujours
ce qui signifie que vous serez toujours averti quel que soit le résultat du statut).
Remarque: lorsque vous spécifiez explicitement un on_failure
ou on_success
vous devez déplacer l'adresse e-mail à l'intérieur de destinataires
clé.
C’est la fin de nos deux parties sur RSpec, TDD et Pry.
Dans la première partie, nous avons réussi à écrire notre application en utilisant le processus TDD et le framework de test RSpec. Dans cette seconde moitié, nous avons également utilisé Pry pour montrer comment déboguer plus facilement une application Ruby en cours d'exécution. Enfin, nous avons pu configurer nos tests de manière à ce qu’ils s’exécutent dans le cadre d’un serveur à intégration continue à l’aide du service Travis-CI..
J'espère que cela vous a donné suffisamment de goût et que vous souhaitez approfondir chacune de ces techniques..