Ponownie wykorzystaj kroki Cucumber

103

Chcę ponownie użyć niektórych kroków Cucumber, ale nie mogę znaleźć właściwej drogi.

Chcę napisać krok taki jak:

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

Ale potem wykonaj kolejny krok, taki jak:

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

Więc testując uwierzytelnianie użytkowników mogę skorzystać z tego pierwszego, ale w większości innych miejsc mogę użyć drugiego i właściwie nie muszę ponownie odtwarzać kodu.

Czy istnieje sposób na wywołanie tego innego kroku, czy po prostu umieszczam logikę w metodzie pomocniczej i wywołuję tę metodę z każdego zadania (w zasadzie refaktoryzacja ekstrakcji metody, która po przeczytaniu mojego pytania sprawia, że ​​wierzę, że to właściwie najlepszy sposób tak czy siak)?

Daniel Huckstep
źródło
1
W przypadku, gdy ktoś jest zdezorientowany, wszyscy tutaj pomijają dowymaganą do rozpoczęcia do...endbloku w definicji kroku Rubiego. W rzeczywistości jest to wymagane.
Shaun Lebron

Odpowiedzi:

102

AKTUALIZACJA : metoda opisana poniżej jest przestarzała. Zalecany sposób wywołania kroku z poziomu innego kroku wygląda teraz następująco:

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

Stara, przestarzała metoda (w celach informacyjnych):

Możesz wywołać kroki z innych kroków, takich jak ten:

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

Jeśli wszystkie scenariusze w ramach funkcji wymagają tego (lub innych kroków), możesz również dodać Tło do każdej funkcji, wykonując typowe czynności, takie jak:

Background:
  Given I log in with valid credentials

Scenario: Change my password
  Given I am on the account page
tomafro
źródło
5
Jeszcze łatwiejsze jest wklejenie kodu korniszona w następujący sposób:steps %Q{Given I am logged in}
BrendanDean,
1
@BrendanDean Kiedy ta odpowiedź została zaakceptowana, stepsmetoda nie istniała. Zobacz moją odpowiedź poniżej.
michaeltwofish
Należy pamiętać, że kroki koniunkcji są teraz uważane za anty-wzór i należy ich unikać. Zobacz Cucumber wiki - cucumber.io/docs/guides/anti-patterns/…
Jan Molak
103

Zwróć uwagę, że metoda wywoływania kroków w krokach zmieniła się w ostatnich wersjach ogórka, co zobaczysz, jeśli pojawi się błąd typu „OSTRZEŻENIE: używanie„ Podano / Kiedy / Wtedy ”w definicjach kroków jest przestarzałe, użyj„ krok ”, aby zamiast tego wywołaj inne kroki: /path/to/step_definitions/foo_steps.rb: 631: in `block in ''. Zobacz wiki ogórka, aby uzyskać szczegółowe informacje.

Istotą zmiany jest to, że należy teraz użyć metod steplub 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
źródło
18
Zresztą warto, po dłuższym czasie z ogórkiem, nie zalecam w ogóle stosowania kroków w krokach. Problemy są trudne do wyśledzenia i faktycznie utrudnia konserwację. Zamiast tego użyj metod pomocniczych.
michaeltwofish
2
Może powinieneś dołączyć ten komentarz do swojej odpowiedzi, ponieważ jest on bardzo pozytywnie oceniany i nadal otrzymuje głosy. Pomoże to ludziom dostrzec te informacje
Andrei Botalov,
cześć @michaeltwofish, czy w 2017 roku nastąpią jakieś zmiany? Otrzymuję syntax error, unexpected tIDENTIFIER, expecting keyword_end stackoverflow.com/questions/43319331/ ...
ericn
43

Nazywanie kroków z definicji kroków jest złą praktyką i ma pewne wady :

  1. Jeśli scenariusz się nie powiedzie i wystąpią zagnieżdżone wywołania kroków, w śladzie stosu zostanie wyświetlona tylko ostatnia wywołana definicja kroku. Może być trudno ustalić, skąd się wzięła ta ostatnia stepdef
  2. Wywołanie stepdef jest czasami trudniejsze do znalezienia i odczytania niż metoda ruby
  3. Metody w języku Ruby dają więcej możliwości niż wywoływanie kroków z definicji kroków

Aslak Hellesøy zaleca wyodrębnienie popularnych działań do świata zamiast ponownego wykorzystywania kroków. Izoluje te akcje w jednym miejscu, dzięki czemu ten kod jest łatwiejszy do znalezienia. Możesz również wyodrębnić kod do zwykłych klas lub modułów Rubiego.

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

Oto przydatna dyskusja na ten temat na liście mailingowej Cucumber - link

Andrei Botalov
źródło
2
Uważam, że to podejście jest znacznie lepsze niż wywoływanie funkcji kroków lub kroków z tych samych powodów, które wymieniono powyżej.
pisaruk
2
Ma to inną zaletę. Używając Idea (lub Rubymine), możesz łatwo przeskoczyć do definicji funkcji, ale nie do kroków w krokach% {...}.
slajd
również ta konfiguracja jest zgodna z zasadą DRY
Sorcerer86pt
2
Chociaż napotkałem problem ponownego użycia kroków, myślę, że jest to po prostu złe. Logowanie to po prostu suma różnych kroków: „odwiedź coś”, „wypełnij coś”. Naturalnym sposobem byłoby ponowne użycie kroków zamiast przekształcania każdego kroku w wywołanie funkcji. IMO, wywołanie kroków w ramach kroków powinno zostać po prostu ulepszone.
dgmora
9

Najlepiej zawijaj kroki w% {} zamiast w cudzysłów. W takim przypadku nie musisz zmieniać cudzysłowów, których będziesz musiał często używać .:

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
źródło
5
To powinien być komentarz zamiast odpowiedzi.
Kelvin,
1

Ponownie użyj słów kluczowych w pliku funkcji, co zapewni możliwość ponownego wykorzystania kodu.

Zdecydowanie NIE zaleca się wywoływania definicji kroków w ramach definicji kroków.

Zapisałbym swój plik funkcji w ten sposób,

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       |

W mojej definicji kroku (to jest 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
    }
}

W ten sposób istnieje wiele możliwości ponownego wykorzystania kodu. Twoje same Given i Then obsługują zarówno prawidłowe, jak i nieprawidłowe scenariusze. Jednocześnie twój plik funkcji ma sens dla czytelników.

LINGS
źródło