objet retourné

Cette mini-série en deux parties a été écrite pour les personnes souhaitant travailler avec Factory Girl et se lancer à la chasse aux objets sans trop chercher dans la documentation. Pour les gens qui ont commencé à jouer avec les tests assez récemment, j'ai fait de mon mieux pour que cela reste convivial pour les débutants..

Évidemment, avoir été dans la même situation à un moment donné m'a fait croire qu'il ne fallait pas grand chose pour que les nouvelles personnes se sentent plus à l'aise avec les tests. Prendre un peu plus de temps pour expliquer le contexte et démystifier le jargon contribue grandement à réduire le taux de frustration des débutants..

Contenu

  • Introduction et contexte
  • Agencements
  • Configuration
  • Définir des usines
  • Utiliser des usines
  • Héritage
  • Plusieurs enregistrements
  • Des séquences

Intro et contexte

Commençons par un peu d’histoire et parlons des gens très bien pensés qui sont responsables de ce bijou Ruby populaire. En 2007/2008, Joe Ferris, CTO de thinkbot, en avait déjà plein les accessoires et a commencé à préparer sa propre solution. Parcourir différents fichiers pour tester une seule méthode était un problème récurrent lors de la manipulation de fixtures. En d'autres termes et en ignorant toutes les inflexibilités, cette pratique conduit également à rédiger des tests qui ne vous en disent pas beaucoup sur le contexte dans lequel le test est effectué..

Ne pas être vendu sur cette pratique actuelle lui a fait chercher différentes solutions pour les usines, mais aucune d’entre elles ne supportait tout ce qu’il voulait. Il a donc proposé Factory Girl, qui a rendu les tests avec des données de test plus lisibles, DRY et également plus explicites en vous donnant le contexte de chaque test. Quelques années plus tard, Josh Clayton, directeur du développement chez thinkbot à Boston, a pris la relève en tant que responsable du projet. Au fil du temps, ce joyau a connu une croissance constante et est devenu un élément incontournable de la communauté Ruby..

Parlons davantage des principaux points douloureux que Factory Girl résout. Lorsque vous construisez votre suite de tests, vous traitez de nombreux enregistrements associés et des informations qui changent fréquemment. Vous souhaitez pouvoir créer des jeux de données pour vos tests d'intégration qui ne soient pas fragiles, faciles à gérer et explicites. Vos usines de données doivent être dynamiques et capables de faire référence à d’autres usines, ce qui est en partie au-delà des appareils YAML d’aujourd’hui..

Une autre commodité que vous souhaitez avoir est la possibilité d'écraser les attributs d'objets à la volée. Factory Girl vous permet de faire tout cela sans effort, étant donné qu'il est écrit en Ruby et que de nombreuses métaprogrammations se déroulent dans les coulisses, et que vous disposez d'un grand langage spécifique à votre domaine et agréable à regarder. aussi.

Construire vos données de montages avec cette gemme peut être décrit comme facile, efficace et globalement plus pratique que de jouer avec les montages. De cette façon, vous pouvez gérer davantage de concepts que de colonnes réelles dans la base de données. Mais assez parlé, parlons un peu,.

Agencements?

Les personnes qui ont de l'expérience dans le test des applications et qui n'ont pas besoin de connaître le concept des appareils, n'hésitez pas à passer à la section suivante. Celui-ci est pour les débutants qui ont juste besoin d'une mise à jour sur le test des données.

Les calendriers sont des exemples de données, c'est tout! Pour une bonne partie de votre suite de tests, vous voulez pouvoir renseigner votre base de données de tests avec des données adaptées à vos scénarios de tests spécifiques. Pendant un bon moment, de nombreux développeurs ont utilisé YAML pour ces données, ce qui les rendait indépendantes de la base de données. Avec le recul, être indépendant de cette façon aurait peut-être été la meilleure chose à ce sujet. C'était plus ou moins un fichier par modèle. Cela seul pourrait vous donner une idée de toutes sortes de maux de tête dont les gens se plaignaient. La complexité est un ennemi en croissance rapide que YAML n’est guère en mesure de prendre efficacement. Ci-dessous, vous verrez ce qu'un tel .yml fichier avec les données de test ressemble à.

Fichier YAML: secret_service.yml

"Quartier-maître ordinaire: nom: Q favori_gadget: compétences en radio balai: inventer des gadgets et le piratage

00 Agent: nom: James Bond favorite_gadget: Sous-marin Lotus Esprit compétences: Obtenir des filles tuées et une infiltration secrète "

Cela ressemble à un hash, n'est-ce pas? Il s'agit d'une liste de paires clé / valeur séparées par des points qui sont séparées par un espace. Vous pouvez référencer d'autres nœuds les uns au-dessus des autres si vous souhaitez simuler des associations à partir de vos modèles. Mais je pense qu'il est juste de dire que c'est là que la musique s'arrête et beaucoup disent que leur douleur commence. Pour les ensembles de données un peu plus complexes, les appareils YAML sont difficiles à maintenir et à modifier sans affecter les autres tests. Vous pouvez les faire fonctionner, bien sûr, après tout, les développeurs les utilisaient beaucoup par le passé, mais beaucoup de personnes ont convenu que le prix à payer pour la gestion des fixtures était un peu trop élevé..

Pour éviter de casser vos données de test lorsque des changements inévitables se produisent, les développeurs ont été heureux d’adopter de nouvelles stratégies offrant davantage de flexibilité et un comportement dynamique. C'est là que Factory Girl est entrée et a laissé les jours de YAML derrière. Un autre problème est la forte dépendance entre le test et la .yml fichier de fixation. Les invités mystères sont également une douleur majeure avec ces types de montages. Factory Girl vous permet d'éviter cela en créant des objets pertinents pour les tests en ligne..

Bien sûr, les appareils YAML sont rapides, et j'ai entendu des gens dire qu’une suite de tests lents avec des données Factory Girl les avait fait retourner au pays YAML. Dans mon esprit, si vous utilisez tellement Factory Girl que cela ralentit vraiment vos tests, vous risquez de trop utiliser les usines et d’ignorer les stratégies qui ne touchent pas la base de données. Vous verrez ce que je veux dire lorsque nous arriverons aux sections pertinentes. Bien sûr, utilisez ce dont vous avez besoin, mais considérez-vous comme averti si YAML vous brûle.

Je pense qu'il serait juste d'ajouter que, dans les débuts de Rails et de Ruby TDD, les appareils YAML constituaient le standard de facto pour la configuration des données de test dans votre application. Ils ont joué un rôle important et ont contribué à faire progresser le secteur. De nos jours, cependant, ils ont un assez mauvais représentant. Les temps changent, passons donc aux usines, qui sont censées remplacer les appareils.

Configuration

Je suppose que Ruby et RSpec for testing sont déjà installés sur votre système. Sinon, revenez après avoir consulté Google et vous devriez être prêt à partir. C'est assez simple, je dirais. Vous pouvez installer le bijou manuellement dans votre terminal via Coquille:

gem bash installer factory_girl

ou l'ajouter à votre Gemfile:

gemme rubis "factory_girl", "~> 4.0" et courir installation groupée.

Maintenant il ne vous reste plus qu'à exiger Factory Girl pour compléter votre installation. Ici, j'utilise RSpec, alors ajoutez ce qui suit en haut de /spec/spec_helper.rb:

ruby nécessite 'factory_girl'

Rubis sur rails

Vous êtes bien sûr couvert si vous souhaitez utiliser Factory Girl with Rails. le factory_girl_rails Gem fournit une intégration pratique de Rails pour ouvrière.

Coquille:

gem bash installer factory_girl_rails

Gemfile:

gemme rubis "factory_girl_rails", "~> 4.0"

et exiger bien sûr dans spec / spec_helper.rb:

ruby nécessite 'factory_girl_rails'

Configuration pratique de la syntaxe

Si vous préférez taper quelque chose comme (bien sûr, vous le faites)

Rubis:

ruby crée (: utilisateur)

au lieu de

ruby FactoryGirl.create (: utilisateur)

chaque fois que vous utilisez l'une de vos usines, il vous suffit d'inclure le FactoryGirl :: Syntaxe :: Méthodes module dans votre fichier de configuration de test. Si vous oubliez cette étape, vous devez précéder toutes les méthodes Factory Girl de la même préface verbeuse. Cela fonctionne avec n'importe quelle application Ruby, pas seulement avec celle de Rails bien sûr.

Pour RSpec trouver le spec / spec_helper.rb déposer et ajouter:

"ruby require 'factory_girl_rails'

RSpec.configure do | config | config.include FactoryGirl :: Syntax :: Methods end "

Attention!

Pour les novices parmi vous, sachez que le RSpec.configure bloc sera déjà là-enterré sous un certain nombre de commentaires. Vous pouvez également faire la même configuration dans un fichier séparé de votre propre spec / support / factory_girl.rb. Dans ce cas, vous devrez bien sûr ajouter le bloc de configuration entier.

La même configuration fonctionne si vous utilisez d'autres bibliothèques pour les tests:

  • Test :: Unité
  • Concombre
  • épinard
  • MiniTest
  • MiniTest :: Spec
  • minitest-rails

Vous pouvez ajouter plus de fantaisie à votre configuration en y ajoutant Nettoyeur de base de données, par exemple, mais la documentation implique que cette configuration est suffisante pour continuer, je vais donc passer à autre chose.

Définir des usines

Vous pouvez définir vos usines n'importe où, mais elles seront chargées automatiquement si elles sont placées aux emplacements suivants:

RSpec:

bash spec / factories.rb spec / usines / *. rb

Test :: Unité:

bash test / usines.rb test / usines / *. rb

Comme vous pouvez le constater, vous avez la possibilité de les scinder en fichiers distincts, conformes à la logique de votre choix, ou de regrouper vos usines dans un seul grand fichier. usines.rb fichier. La complexité de votre projet sera le meilleur moyen de déterminer quand séparer logiquement des usines dans leurs propres fichiers séparés..

Usines nues

Factory Girl fournit une syntaxe Ruby DSL bien développée pour définir des usines telles que utilisateur, poster ou tout autre objet, pas seulement Enregistrement actif objets. Les classes de rubis «simples» vont parfaitement bien. Vous commencez par configurer un bloc de définition dans votre usines.rb fichier.

Rubis:

"ruby FactoryGirl.define faire

fin"

Toutes les usines sont définies dans ce bloc. Les usines ont juste besoin d'un :symbole nom et un ensemble d'attributs pour commencer. Ce nom doit être le snake_cased version du modèle que vous voulez émuler, comme SecretServiceAgent dans l'exemple suivant. L'usine ci-dessous est nommée secret_service_agent et a des attributs appelés prénom, favori_gadget et compétences.

Rubis:

"ruby FactoryGirl.define faire

factory: secret_service_agent nommez «Q» favorite_gadget compétences «sous-marin Lotus Esprit», «inventer des gizmos et pirater», fin

fin"

Attention!

Si vous supprimez un élément de cet article, il devrait être le suivant: Définissez uniquement la fabrique la plus simple possible afin d'être valide par défaut, dans le sens des validations Active Record, par exemple..

Quand Factory Girl appelle enregistrer! dans certains cas, vos validations seront exercées. Si l'un d'entre eux échoue, ActiveRecord :: RecordInvalid se soulève. Définir uniquement le strict minimum vous donne plus de flexibilité si vos données changent et réduira les risques d'échec des tests et de la duplication, car le couplage est réduit au strict minimum. Ne soyez pas paresseux lorsque vous composez vos usines, cela vous rapportera beaucoup!

Si vous pensez que cela semble difficile à gérer, vous serez probablement ravi d'apprendre qu'il existe des solutions pratiques pour séparer les objets et leurs attributs.. Héritage et Traits deviendront de grands alliés car ce sont des stratégies pratiques pour compléter vos usines nues et les garder au sec en même temps. En savoir plus sur l'héritage ci-dessous, et mon deuxième article se concentrera un peu sur les traits.

Utiliser des usines

Si vous avez inclus le FactoryGirl :: Syntaxe :: Méthodes dans l'étape de configuration, vous pouvez utiliser la syntaxe abrégée pour créer des fabriques dans vos tests. Vous avez quatre options, que les gens appellent élaborer des stratégies:

créer

ruby create (: some_object) # FactoryGirl.create (: some_object)

Celui-ci retourne une instance de la classe émulée par la fabrique. Il est recommandé d'utiliser créer seulement quand vous avez vraiment besoin de frapper la base de données. Cette stratégie ralentit votre suite de tests si elle est utilisée inutilement. Utilisez-le si vous voulez exécuter vos validations car il fonctionnera enregistrer! en arrière-plan. Je pense que cette option est surtout appropriée lorsque vous effectuez des tests d'intégration lorsque vous souhaitez impliquer une base de données pour vos scénarios de test..

construire

ruby build (: some_object) # FactoryGirl.build (: some_object)

Il instancie et attribue des attributs, mais vous obtiendrez une instance qui ne sera pas enregistrée dans la base de données.-construire conserve l'objet uniquement en mémoire. Si vous voulez vérifier si un objet a certains attributs, cela fonctionnera, car vous n'avez pas besoin d'un accès à la base de données pour ce genre de choses. Dans les coulisses, il ne utilise pas enregistrer!, ce qui signifie que vos validations ne sont pas exécutées.

La tête haute!

Lorsque vous utilisez des associations avec elle, vous risquez de tomber dans un piège. Il y a une petite exception concernant le fait de ne pas sauvegarder l'objet via construire-il construit l'objet mais crée les associations - que je couvrirai dans la section sur les associations de Factory Girl. Il existe une solution pour cela au cas où ce ne serait pas ce que vous recherchiez.

build_stubbed

ruby build_stubbed (: some_object) # FactoryGirl.build_stubbed (: some_object)

Cette option a été créée pour accélérer vos tests et pour les cas de test pour lesquels aucun code n'a besoin de toucher la base de données. Il instancie également et attribue des attributs comme construire fait, mais cela ne fait que donner l'impression que les objets ont été persistés. L'objet retourné a tous les attributs définis de votre usine stubbed out plus un faux identifiant et néant horodatages. Leurs associations sont écrasées de la même façon construire associations qui utilisent créer sur les objets associés. Comme cette stratégie traite les stubs et ne dépend pas de la base de données, ces tests seront aussi rapides que possible..

attributs_pour

Cette méthode ne renverra qu'un hachage des attributs définis dans la fabrique appropriée, sans associations, horodatages et id bien sûr. C'est pratique si vous voulez créer une instance d'un objet sans manipuler manuellement les hachages d'attributs. Je l'ai vu principalement utilisé dans les spécifications du contrôleur similaires à ceci:

Rubis:

"ruby it" redirige vers un emplacement donné "ne post: créer, espionner: attributs_pour (: espion)

attend (réponse) .to redirect_to (some_location) end "

Comparaison d'objets

Pour terminer cette section, permettez-moi de vous donner un exemple simple pour voir les différents objets renvoyés par ces quatre stratégies de construction. Ci-dessous, vous pouvez comparer les quatre objets différents que vous obtiendrez. attributs_pour, créer, construire et build_stubbed:

"ruby FactoryGirl.define faire

usine: le nom de l'espion “Marty McSpy” favorite_gadget “Hoverboard”: compétences “Infiltration et espionnage”

fin"

"ruby attributs_for (: espion)

objet retourné

"

"ruby crée (: espion)

objet retourné

"

"build ruby ​​(: espion)

objet retourné

"

"ruby build_stubbed (: spy)

objet retourné

"

J'espère que cela vous a aidé si vous aviez encore du mal à comprendre comment ils fonctionnent et quand utiliser quelle option.

Héritage

Vous allez aimer celui-ci! Avec l'héritage, vous ne pouvez définir des fabriques qu'avec les attributs nécessaires à la création de chaque classe. Cette usine parente peut générer autant d'usines «enfants» que vous le souhaitez, pour couvrir tous les types de scénarios de test avec différents ensembles de données. Garder vos données de test au sec est très important, et l'héritage vous facilite grandement la tâche..

Supposons que vous souhaitiez définir quelques attributs principaux dans une usine et que, dans cette même usine, différentes usines soient affectées au même Classe avec des attributs différents. Dans l'exemple ci-dessous, vous pouvez voir comment éviter de répéter des attributs en imbriquant simplement vos usines. Imitons un Espion classe qui doit s'adapter à trois scénarios de test différents.

usines.rb

"ruby FactoryGirl.define faire

usine: les espions appellent 'Marty McSpy' licence_to_kill de fausses compétences 'Espionnage et intelligence'

factory: quartermaster nommez 'Q' compétences 'Inventez des gizmos et piratez' end usine ': obligez vous nommez' James Bond 'licence_to_kill vrai fin fin 

fin"

some_spec.rb

"lien ruby ​​= créer (: lien) quartermaster = créer (: quartier maître)

quartermaster.name # => 'Q' quartermaster.skills # => 'Inventer des gizmos et bidouiller' quartermaster.licence_to_kill # => false

bond.name # => 'James Bond' bond.skills # => 'Espionnage et intelligence' bond.licence_to_kill # => true "

Comme vous pouvez le constater, le :liaison et :intendant les usines héritent des attributs de leur parent :espion. Dans le même temps, vous pouvez facilement écraser les attributs si nécessaire et être très expressif à ce sujet via leurs noms d'usine. Imaginez toutes les lignes de code enregistrées car vous n'avez pas besoin de répéter la même configuration de base si vous souhaitez tester différents états ou objets associés. Cette fonctionnalité à elle seule vaut la peine d’utiliser Factory Girl et rend difficile le retour aux appareils YAML..

Si vous souhaitez éviter l’imbrication des définitions de fabrique, vous pouvez également lier explicitement les fabriques à leur père en fournissant un parent hacher:

usines.rb

"fabrique de rubis: les espions se nomment" Marty McSpy "licence_to_kill" compétences fausses "fin" Espionnage et intelligence "

usine: lien, parent:: espion nommez 'James Bond' licence_to_kill true end "

C'est la même fonctionnalité et presque comme DRY.

Plusieurs enregistrements

Voici un petit ajout, mais néanmoins bienvenu, à Factory Girl qui facilite la gestion des listes:

  • liste de construction
  • créer une liste

De temps en temps, dans les situations où vous souhaitez avoir plusieurs instances d'une usine sans beaucoup de fuzz, celles-ci vous seront utiles. Les deux méthodes renverront un tableau avec la quantité d'éléments d'usine spécifiée. Plutôt chouette?

Rubis:

"ruby spy_clones = create_list (: spy, 3)

fake_spies = liste de construction (: spy, 3)

spy_clones # => [#, #, #]

fake_spies # => [#, #, #] "

Il existe des différences subtiles entre les deux options, mais je suis sûr que vous les comprenez maintenant. Je devrais également mentionner que vous pouvez fournir aux deux méthodes un hachage d'attributs si vous souhaitez écraser les attributs d'usine à la volée pour une raison quelconque. Les écrasements consommeront un peu de votre vitesse de test si vous créez beaucoup de données de test avec écrasements. Avoir probablement une usine séparée avec ces attributs modifiés sera une meilleure option pour créer des listes.

"ruby smug_spies = create_list (: spy, 3, compétences: 'Blag blagues')

double_agents = build_list (: spy, 3, name: 'Vesper Lynd', compétences: 'Seduction and bookkeeping')

smug_spies # => [#, #, #]

double_agents # => [#, #, #] "

Vous aurez souvent besoin d’une paire d’objets. Factory Girl vous propose donc deux autres options:

  • build_pair
  • create_pair

C'est la même idée que ci-dessus, mais le tableau retourné ne contient que deux enregistrements à la fois..

"ruby create_pair (: spy) # => [#, #]

build_pair (: spy) # => [#, #] "

Des séquences

Si vous pensiez que nommer des espions pourrait être moins statique, vous avez absolument raison. Dans cette dernière section, nous examinerons la création de valeurs uniques séquentielles en tant que données de test pour vos attributs d'usine..

Quand cela pourrait-il être utile? Qu'en est-il des adresses électroniques uniques pour tester l'authentification ou des noms d'utilisateur uniques, par exemple? C'est là que brillent les séquences. Examinons différentes manières d’utiliser ce qui suit: séquence:

Séquence "globale"

"ruby FactoryGirl.define faire

séquence: spy_email do | n | “00#[email protected]” end

usine: espion ne nommer 'Marty McSpy' email '[email protected]' compétences 'Espionnage et infiltration' license_to_kill false

factory: elite_spy nommez 'Edward Donne' license_to_kill vrai fin fin 

fin

top_spy = create (: elite_spy) top_spy.email = générer (: spy_email)

top_spy.email # => “[email protected]” "

Étant donné que cette séquence est définie «globalement» pour toutes vos usines (elle n'appartient pas à une usine spécifique), vous pouvez utiliser ce générateur de séquence pour toutes vos usines, où vous en avez besoin. : spy_email. Tout ce dont tu as besoin c'est produire et le nom de votre séquence.

Les attributs

En tant que petite variante très pratique, je vais vous montrer comment affecter directement des séquences en tant qu'attributs à vos usines. Utilisez la même condition que ci-dessus, où votre séquence est définie «globalement». Dans le cas d’une définition d’usine, vous pouvez laisser la produire méthode call et Factory Girl affecteront la valeur renvoyée par la séquence directement à l'attribut du même nom. Soigné!

"ruby FactoryGirl.define faire

séquence: email do | n | “00#[email protected]” end

factory: secret_service_agent nommez les adresses e-mail 'Edwad Donne' dans 'Espionnage et infiltration' license_to_kill true end

fin"

Attributs paresseux

Vous pouvez également utiliser un séquence sur attributs paresseux. Je traiterai de ce sujet dans mon deuxième article, mais par souci d'exhaustivité, je voulais également le mentionner ici. Si vous avez besoin d’une valeur unique attribuée au moment de la création de l’instance, elle est appelée paresseux parce que cet attribut attend avec l'attribution de valeur jusqu'à ce que les séquences d'instanciation d'objet aient juste besoin d'un bloc pour fonctionner également.

"rubis

FactoryGirl.define do

séquence: mission_deployment do | number | “Mission # numéro à # DateTime.now.to_formatted_s (: short)” fin

factory: spy do nomme 'Marty McSpy' déploiement generate (: mission_deployment) fin

fin

some_spy = create (: spy) some_spy.deployment # => “Mission n ° 1 au 19 oct. 21h13” "

Le bloc de l'attribut factory est évalué lorsque l'objet est instancié. Dans notre cas, vous aurez une chaîne composée d’un numéro de mission unique et d’un nouveau Date et heure objet en tant que valeurs pour chaque :espion qui se déploie.

Séquence en ligne

Cette option est préférable lorsqu'une séquence de valeurs uniques n'est requise que sur un attribut pour une seule usine. Cela n'aurait aucun sens de le définir en dehors de cette usine et de devoir le chercher ailleurs si vous avez besoin de le peaufiner. Dans l'exemple ci-dessous, nous cherchons à générer des identifiants uniques pour chaque voiture espion..

"ruby FactoryGirl.define faire

factory: aston_martin do sequence (: vehicle_id_number) | n | “A_M _ # n” end

fin

spycar_01 = créer (: aston_martin) spycar_01.vehicle_id_number # => “A_M_1”

spycar_02 = créer (: aston_martin) spycar_02.vehicle_id_number # => “A_M_2” "

Eh bien, nous devrions peut-être fournir le Vehicle_id_number attribuer une autre valeur de départ que 1? Supposons que nous voulions prendre en compte quelques prototypes avant que la voiture ne soit prête pour la production. Vous pouvez fournir un deuxième argument comme valeur de départ pour votre séquence. Allons avec 9 cette fois.

"ruby FactoryGirl.define faire

factory: aston_martin do sequence (: vehicle_id_number, 9) | n | “A_M _ # n” end

fin

spycar_01 = créer (: aston_martin) spycar_01.vehicle_id_number # => “A_M_9”

spycar_02 = créer (: aston_martin) spycar_02.vehicle_id_number # => “A_M_10”

"

Pensées finales

Comme vous l'avez déjà vu, Factory Girl propose un Ruby DSL bien équilibré qui construit des objets au lieu des enregistrements de base de données pour vos données de test. Il est utile de garder vos tests concentrés, secs et lisibles lorsque vous traitez avec des données factices. C'est un bel accomplissement dans mon livre.

N'oubliez pas que les définitions d'usine nues sont la clé de votre santé future. Plus vous mettez de données d'usine dans votre espace de test global, plus vous rencontrerez de difficultés de maintenance..

Pour vos tests unitaires, Factory Girl sera inutile et ne fera que ralentir votre suite de tests. Josh Clayton serait le premier à en témoigner et recommandait d'utiliser de manière sélective et minimale le logiciel Factory Girl..