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
Show archive.org snapshot
(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