Reutilizar los pasos del pepino

103

Quiero reutilizar algunos pasos de pepino, pero parece que no puedo encontrar el camino correcto.

Quiero escribir un paso como:

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

Pero luego da otro paso como:

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

Entonces, al probar la autenticación de usuario, puedo usar la primera, pero en la mayoría de los otros lugares, puedo usar la última y no tener que reprogramar el código.

¿Hay alguna manera de llamar a ese otro paso, o simplemente pongo la lógica en un método auxiliar y llamo a dicho método desde cada tarea (básicamente una refactorización de extracción de método, que, después de leer mi pregunta, me hace creer que es en realidad la mejor manera) de todas formas)?

Daniel Huckstep
fuente
1
En caso de que alguien esté confundido, todos aquí están omitiendo lo donecesario para iniciar el do...endbloque en la definición del paso de Ruby. De hecho, es necesario.
Shaun Lebron

Respuestas:

102

ACTUALIZACIÓN : El método que se describe a continuación ha quedado obsoleto. La forma recomendada de llamar a un paso desde dentro de otro paso ahora se ve así:

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

Método antiguo y obsoleto (para referencia):

Puede llamar a pasos de otros pasos como este:

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

Si todos los escenarios dentro de una característica requieren esto (u otros pasos), también puede agregar un Fondo a cada característica, con los pasos comunes, así:

Background:
  Given I log in with valid credentials

Scenario: Change my password
  Given I am on the account page
tomafro
fuente
5
Aún más fácil es pegar el código de pepinillo así:steps %Q{Given I am logged in}
BrendanDean
1
@BrendanDean Cuando se aceptó esta respuesta, el stepsmétodo no existía. Vea mi respuesta a continuación.
michaeltwofish
Tenga en cuenta que los pasos de conjunción ahora se consideran un anti-patrón y deben evitarse. Ver la wiki de Pepino - cucumber.io/docs/guides/anti-patterns/…
Jan Molak
103

Tenga en cuenta que el método para llamar a los pasos dentro de los pasos ha cambiado en las versiones recientes de pepino, que verá si obtiene un error como "ADVERTENCIA: El uso de 'Dado / Cuando / Entonces' en las definiciones de pasos está obsoleto, use 'paso' para llame a otros pasos en su lugar: /path/to/step_definitions/foo_steps.rb: 631: in `block in '". Consulte la wiki de pepino para obtener más detalles.

La esencia del cambio es que ahora debería utilizar los métodos stepo 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
fuente
18
Por lo que vale, después de más tiempo con Pepino, recomiendo no usar pasos dentro de pasos en absoluto. Los problemas son difíciles de localizar y, de hecho, dificultan el mantenimiento. En su lugar, utilice métodos auxiliares.
michaeltwofish
2
Tal vez, debería incluir este comentario en su respuesta, ya que está muy a favor y todavía recibe votos. Ayudará a las personas a notar esta información
Andrei Botalov
hola @michaeltwofish, ¿hay algún cambio en esto en 2017? Recibo syntax error, unexpected tIDENTIFIER, expecting keyword_end stackoverflow.com/questions/43319331/…
ericn
43

Llamar a pasos a partir de definiciones de pasos es una mala práctica y tiene algunas desventajas :

  1. Si el escenario falla y hay invocaciones de paso anidadas, solo obtendrá la última definición de paso invocada en el seguimiento de la pila. Puede ser difícil encontrar desde qué lugar se llamó la última definición de paso.
  2. La llamada a stepdef a veces es más difícil de encontrar y leer que el método ruby
  3. Los métodos Ruby le dan más poder que llamar a pasos desde defs de paso

Aslak Hellesøy recomienda extraer acciones populares a World en lugar de reutilizar los pasos. Aísla esas acciones en un solo lugar, hace que este código sea más fácil de encontrar. También puede extraer código a clases o módulos Ruby habituales.

#/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

Aquí hay una discusión útil sobre el tema en la lista de correo de Cucumber - enlace

Andrei Botalov
fuente
2
Creo que este enfoque es mucho mejor que llamar a las funciones de pasos o pasos por las mismas razones mencionadas anteriormente.
pisaruk
2
Esto tiene otro beneficio. Con Idea (o Rubymine), puede saltar fácilmente a las definiciones de funciones, pero no a los pasos de los pasos% {...}.
slipset
también esta configuración sigue el principio DRY
Sorcerer86pt
2
Aunque me encontré con el problema de reutilizar los pasos, creo que esto es simplemente malo. El inicio de sesión es solo la suma de diferentes pasos: "visitar algo", "llenar algo". La forma natural sería reutilizar los pasos, en lugar de convertir cada paso en una llamada a una función. En mi opinión, los pasos dentro de los pasos deben mejorarse.
dgmora
9

Es mejor incluir los pasos en% {} en lugar de comillas. Entonces, no necesita escapar de las comillas dobles que deberá usar con frecuencia:

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
Rimiano
fuente
5
Esto debería haber sido un comentario en lugar de una respuesta.
Kelvin
1

Reutilice las palabras clave en el archivo de características que proporcionará la reutilización del código.

NO es muy recomendable llamar step defs dentro de step defs.

Escribiría mi archivo de características de esta manera,

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       |

En mi definición de paso, (esto es 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 esta manera, hay mucha reutilización de código. Su mismo Given and Then maneja escenarios válidos e inválidos. Al mismo tiempo, su archivo de características tiene sentido para los lectores.

LINGS
fuente