Réutiliser les étapes de concombre

103

Je veux réutiliser certaines étapes du concombre mais je n'arrive pas à trouver le bon chemin.

Je veux écrire une étape comme:

Given /^I login with (.*) credentials$/ |type|
  # do stuff with type being one of "invalid" or "valid"
end

Mais alors ayez une autre étape comme:

Given /^I login successfully$
  # call "Given I login with valid credentials"
end

Donc, en testant l'authentification des utilisateurs, je peux utiliser le premier, mais dans la plupart des autres endroits, je peux utiliser le second, sans avoir à reproduire le code.

Existe-t-il un moyen d'appeler cette autre étape, ou est-ce que je mets simplement la logique dans une méthode d'aide et appelle-t-elle ladite méthode à partir de chaque tâche (essentiellement un refactoring d'extraction de méthode, qui, après avoir lu ma question, me fait croire que c'est en fait le meilleur moyen en tous cas)?

Daniel Huckstep
la source
1
Au cas où quelqu'un serait confus, tout le monde ici laisse de côté le donécessaire pour démarrer le do...endbloc dans la définition de l'étape Ruby. C'est en fait obligatoire.
Shaun Lebron

Réponses:

102

MISE À JOUR : la méthode décrite ci-dessous est obsolète. La méthode recommandée pour appeler une étape à partir d'une autre étape ressemble maintenant à ceci:

Given /^I login successfully$/
    step "I login with valid credentials" 
end 

Ancienne méthode obsolète (pour référence):

Vous pouvez appeler des étapes d'autres étapes comme celle-ci:

Given /^I login successfully$/
  Given "I login with valid credentials"
  Then "I should be logged in"
end

Si tous les scénarios d'une fonctionnalité nécessitent cette étape (ou d'autres étapes), vous pouvez également ajouter un arrière-plan à chaque fonctionnalité, avec les étapes courantes, comme ceci:

Background:
  Given I log in with valid credentials

Scenario: Change my password
  Given I am on the account page
tomafro
la source
5
Il est encore plus facile de coller le code du cornichon comme steps %Q{Given I am logged in}
suit
1
@BrendanDean Lorsque cette réponse a été acceptée, la stepsméthode n'existait pas. Voir ma réponse ci-dessous.
michaeltwofish
Veuillez noter que les étapes de conjonction sont désormais considérées comme un anti-pattern et doivent être évitées. Voir le wiki du concombre - cucumber.io/docs/guides/anti-patterns/...
Jan Molak
103

Notez que la méthode d'appel des étapes dans les étapes a changé dans les versions récentes de concombre, ce que vous verrez si vous obtenez une erreur du type "AVERTISSEMENT: l'utilisation de 'Compte tenu / Quand / Alors' dans les définitions d'étape est obsolète, utilisez 'step' pour appelez d'autres étapes à la place: /path/to/step_definitions/foo_steps.rb: 631: in `block in '". Voir le wiki du concombre pour plus de détails.

L'essentiel du changement est que vous devez maintenant utiliser les méthodes stepou steps.

When /^I make all my stuff shiny$/
  step "I polish my first thing"
end

When /^I make all my stuff shiny$/
  steps %Q{
    When I polish my first thing
    When I shine my second thing
  }
end
michaeltwofish
la source
18
Pour ce que ça vaut, après plus de temps avec Cucumber, je recommande de ne pas utiliser du tout les étapes par étapes. Les problèmes sont difficiles à dépister et cela rend la maintenance plus difficile. Utilisez plutôt des méthodes d'assistance.
michaeltwofish
2
Vous devriez peut-être inclure ce commentaire dans votre réponse car il est très apprécié et reçoit toujours des votes. Cela aidera les gens à remarquer cette information
Andrei Botalov
salut @michaeltwofish, y a-t-il un changement à cela en 2017? Je reçois syntax error, unexpected tIDENTIFIER, expecting keyword_end stackoverflow.com/questions/43319331/…
ericn
43

L'appel d'étapes à partir de définitions d'étape est une mauvaise pratique et présente certains inconvénients :

  1. Si le scénario échoue et qu'il existe des appels d'étape imbriqués, vous n'obtiendrez que la dernière définition d'étape appelée dans la trace de pile. Il peut être difficile de trouver à partir de quel endroit ce dernier stepdef a été appelé
  2. L'appel à stepdef est parfois plus difficile à trouver et à lire que la méthode ruby
  3. Les méthodes Ruby vous donnent plus de puissance que d'appeler des étapes à partir des définitions d'étape

Aslak Hellesøy recommande d'extraire les actions populaires dans World au lieu de réutiliser les étapes. Il isole ces actions en un seul endroit, facilite la recherche de ce code. Vous pouvez également extraire du code vers des classes ou des modules Ruby habituels.

#/support/world_extensions.rb
module KnowsUser
  def login
    visit('/login')
    fill_in('User name', with: user.name)
    fill_in('Password', with: user.password)
    click_button('Log in')
  end

  def user
    @user ||= User.create!(:name => 'Aslak', :password => 'xyz')
  end
end
World(KnowsUser)

#/step_definitions/authentication_steps.rb
When /^I login$/ do
  login
end

Given /^a logged in user$/ do
  login
end

Voici une discussion utile sur le sujet dans la liste de diffusion Cucumber - lien

Andrei Botalov
la source
2
Je pense que cette approche est bien meilleure que d'appeler des fonctions step ou steps pour les mêmes raisons mentionnées ci-dessus.
pisaruk
2
Cela a un autre avantage. En utilisant Idea (ou Rubymine), vous pouvez facilement passer aux définitions de fonction, mais pas aux étapes des étapes% {...}.
Slipset
également cette configuration suit le principe DRY
Sorcerer86pt
2
Même si j'ai rencontré le problème de la réutilisation des étapes, je pense que c'est tout simplement mauvais. La connexion est juste la somme des différentes étapes: «visiter quelque chose», «remplir quelque chose». La manière naturelle serait de réutiliser les étapes, au lieu de convertir chaque étape en un appel à une fonction. OMI, l'appel des étapes à l'intérieur des étapes devrait simplement être amélioré.
dgmora le
9

Mieux vaut encapsuler vos étapes en% {} plutôt qu'en guillemets. Ensuite, vous n'avez pas besoin d'échapper aux guillemets doubles que vous devrez utiliser fréquemment:

Given /^I login successfully$
  step %{I login with valid credentials}
end

Given /^I login with (.*) credentials$/ |type|
  # do stuff with type being one of "invalid" or "valid"
end
Rimian
la source
5
Cela aurait dû être un commentaire plutôt qu'une réponse.
Kelvin
1

Réutilisez les mots-clés dans le fichier d'entités qui fourniront la réutilisabilité du code.

Il n'est pas recommandé d'appeler step defs dans step defs.

J'écrirais mon fichier d'entités de cette façon,

Scenario Outline: To check login functionality
    Given I login with "<username>" and "<password>"
    Then I "<may or may not>" login successfully

Examples:
    |username|password|may or may not|
    |paul    |123$    |may           |
    |dave    |1111    |may not       |

Dans ma définition d'étape, (c'est Java)

@Given(I login with \"([^\"]*)\" and \"([^\"]*)\"$)
public void I_login_with_and(String username, String password){

   //login with username and password

}

@Then(I \"([^\"]*)\" login successfully$)
public void I_login_successully_if(String validity){

    if(validity.equals("may")){
        //assert for valid login
    }
    else
    if(validity.equals("may not")){
        //assert for invalid login
    }
}

De cette façon, il y a beaucoup de réutilisabilité du code. Votre même Étant donné et Alors gère les scénarios valides et non valides. En même temps, votre fichier d'entités a du sens pour les lecteurs.

LINGS
la source