Posted almost 4 years ago. Visible to the public. Deprecated.

Beware: Nested Spreewald patiently blocks are not patient

Nested Spreewald patiently blocks are now patient (version 1.10.4+)

Note: The behaviour of Spreewald's within step is as described below for version < 1.9.0; For Spreewald >= 1.9.0 it is as described in Solution 1.

When doing integration testing with cucumber and selenium you will often encounter problems with timing - For example if your test runs faster than your application, html elements may not yet be visible when the test looks for them. That's why Spreewald (a collection of cucumber steps) has a concept of doing things patiently, which means a given block that fails will be retried for a certain amount of time. Only if the time is up and the block still fails an error is thrown. This reduces flickering of tests.

From Spreewald's tolerance_for_selenium_sync_issues.rb:

def patiently(seconds = CapybaraWrapper.default_max_wait_time, &block) old_wait_time = CapybaraWrapper.default_max_wait_time # dont make nested wait_untils use up all the alloted time CapybaraWrapper.default_max_wait_time = 0 # for we are a jealous gem ... # call block and retry if necessary and time allows ensure CapybaraWrapper.default_max_wait_time = old_wait_time end

This means all patiently-calls that are nested within another patiently call are not patiently. They do never retry because the wait time for them is 0. It seems like a good idea not to "waste" all the time for retries in the most deeply nested patiently-block, but it may lead to unexpected behaviour, especially if the outermost patiently is "hidden".

For example consider the following step, which wants to interact with a "picker"-element. As there are multiple pickers on the page, they are wrapped in divs with numbers so that the test knows which one to operate:

# Feature When I pick "Lions" from the album picker within ".media-picker-1"
# Step definition When /^I pick "(.+?)" from the album picker$/ do |label| ... # open the picker and choose an item patiently do ... # close picker and ensure the correct item is selected end end

Looks good, right? - Well, but unfortunately the patiently in the step does nothing because it's nested! Spreewald's within-step will use patiently. The given selector may not yet be visible - maybe it is appended via ajax or the like and therefore the step will patiently wait until the element appears. From Spreewald's web_steps.rb:

When /^(.*) within (.*[^:])$/ do |nested_step, parent| patiently do with_scope(parent) { step(nested_step) } end end

Therefore all patiently calls of the step itself have a wait time of 0 - they are not patiently at all.

Solution 1

This is the preferred solution.
Just don't nest. Refactor the outer patiently to really only wrap the part of the code that needs to be patiently. For the example with Spreewald's within-step a better solution would be to wrap the patiently only around a check that the parent element is visible and then (not patiently) call the step within that scope:

When /^(.*) within (.*[^:])$/ do |nested_step, parent| patiently do page.should have_css(_selector_for(parent)) end with_scope(parent) { step(nested_step) } end

Spreewald implements this behaviour since 1.9.0

Solution 2

If the outer patiently is not within your reach, you can explicitly tell the inner patiently how long it should wait (in seconds). This overrides the default. Its a good idea to make the inner wait time shorter than the outer wait time, otherwise the outer patiently can not retry if the inner patiently fails. E.g.:

When /^I pick "(.+?)" from the album picker$/ do |label| ... # open picker and choose an item patiently 2 do ... # close picker and ensure the correct item is selected end end

makandra has been working exclusively with Ruby on Rails since 2007. Our laser focus on a single technology has made us a leader in this space.

Owner of this card:

Judith Roth
Last edit:
almost 4 years ago
by Henning Koch
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Judith Roth to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more