Search code examples
rubycucumbercalabashcalabash-ios

Share @variable in step_definitions and PageObject methods possible?


Is there an easy way to reference @current_page in both the step_definition.rb files and the PageObject.rb files?

Examples using the @current_page in the Calabash docs force you to always do something like:

@current_page = @current_page.login(user)

After login you are on a new page and you must pass back a reference to this for the @current_page. It would be nice to just set @current_page in the login method and not have to do that @current_page = @current_page.login(user) assignment every time you call a method that brings you to a new page.

Is there a good way to do this?


Solution

  • Is there a good way to do this?

    It is not recommended that you keep a reference to the current page.

    I could demonstrate a number of ways to keep a reference, but I don't want to because it is not a good pattern.

    At risk of getting my response marked as a non-answer, I will try to explain why.

    Examples using the @current_page in the Calabash docs force you to always do something like:

    In my opinion, the docs are not good. I have been trying to get them changed. There is general agreement on this topic among the Calabash developers, but none of us has had the time to change them.

    It is not a best practice to use @current_page to track the current page between steps. The reason is that the reader can never know what the value of the @current_page is just by looking at it: it could have been set to anything by subsequent step.

    The best practice is to create a temporary page object whenever you need one.

    # Bad
    @current_page = @current_page.login(user)
    
    # Good
    login_page = page(LoginPage).await
    some_other_page = login_page.login(user)
    some_other_page.await
    

    force you to always do something like:

    The @current_page is a Cucumber World variable; there is nothing special about it. It could have been called: @page or @screen or @foobar. When I say there is nothing special I mean that Calabash does not use @current_page anywhere internally.

    It would be nice to just set @current_page in the login method and not have to do that @current_page = @current_page.login(user) assignment every time you call a method that brings you to a new page.

    Is there a good way to do this?

    In general, it is not a good idea save state in your cucumber tests or page model. If you need a piece of information in a Step or method, you should ask for it by querying the app.

    There are, of course, exceptions. Imagine an app with a Dashboard page that has a Reminders icon with badge count that represents the number of unread reminders.

     Scenario:  Verify the reminders badge count
       Given I am looking at the Dashboard
       And I make a note of the reminders badge count
       When I go to the reminders page
       Then the reminders badge count should match the unread reminders
    

    It is reasonable to store the badge count in a Cucumber World variable so you can use it in a subsequent Step.

     And(/^I make a note of the reminders badge count$/) do
       dashboard = page(Dashboard).await
       @reminder_badge_count = dashboard.reminder_badge_count
     end
    
     When(/^I go to the reminders page$/) do
       dashboard = page(Dashboard).await
       dashboard.navigate_to(:reminders)
     end
    
     Then(/^the reminders badge count should match the unread reminders$/) do
       reminders_page = page(Reminders).await
       unread_actual = reminders_page.unread_reminders
       unless unread_actual == @reminder_badge_count
         screenshot_and_raise("Expected badge count '#{@reminder_badge_count}' to match unread count '#{unread_actual}") 
       end
     end