编写系统测试有时会很棘手,因为我们想断言一个尚未呈现的现实。临时睡觉是可以大部分解决这个问题的,但它不是最优雅的解决方案,如果可能的话应该避免。
问题所在
当我们用Capybara编写测试时,我们可能会要求一个需要较长时间才能表现出来的动作。
想象一下,一个更新记录的表单提交。
page.fill_in :data, with: "foo bar"
page.click_on "Update"
# test the expectation
assert page.has_content?("Success")
在这种情况下,Capybara为我们提供了保障。has_content? ,我们会等待。
如果我们想断言相反的情况呢?
# ...
assert_not page.has_content?("Failure")
不幸的是,这段代码不会像预期的那样工作。一个自然的快速修复方法是使用sleep 。
# ...
sleep 0.5
assert_not page.has_content?("Failure")
同样地,其他断言也有同样的问题。
assert_not page.has_css?(".flash")
修复方法
修复大多很容易。使用Capybara的替代品来断言否定是很重要的。
# will wait
assert page.has_no_content?("Success")
# will wait
assert page.has_no_css?(".flash")
以类似的方式,我们应该依靠has_no_field?,has_no_link?,has_no_table?, 和其他Capybara的否定断言。
我们还可以用default_max_wait_time ,调整最大的等待时间。
Capybara.default_max_wait_time = ENV.fetch("MAX_WAIT_TIME_IN_SECONDS", 2).to_i
如果我们需要增加这个数字只是为了一个测试,有using_wait_time ,它需要的是秒数。
# in test
using_wait_time(5) do
assert page.has_no_css?(".flash")
end
如果我们需要用Capybara帮助器以外的东西来使用sleep ,我们可以写一个帮助器,睡到预定的等待时间。
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
...
def wait_until(time: Capybara.default_max_wait_time)
Timeout.timeout(time) do
until value = yield
sleep(0.1)
end
value
end
end
# in test
wait_until(time: 2.5) do
page.page_path == current_path
end
结论
在Rails系统测试中,我们无法真正避免睡眠,但我们应该让Capybara做繁重的工作,跳过用随机的sleep 调用来污染我们的测试。另外,sleep 是一个工具。如果你打开一个Capybara测试套件,你会发现临时调用sleep 。