Posted 4 months ago. Visible to the public.

Beware: Nested Spreewald patiently blocks are not patient

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:

Copy
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:

Copy
# Feature When I pick "Lions" from the album picker within ".media-picker-1"
Copy
# 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:

Copy
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:

Copy
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.:

Copy
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

Does your version of Ruby on Rails still receive security updates?
Rails LTS provides security patches for old versions of Ruby on Rails (3.2 and 2.3).

Author of this card:

Avatar
Judith Roth
Last edit:
3 months 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 makandropedia