如何避免Rails系统测试中的睡眠

81 阅读2分钟

编写系统测试有时会很棘手,因为我们想断言一个尚未呈现的现实。临时睡觉是可以大部分解决这个问题的,但它不是最优雅的解决方案,如果可能的话应该避免。

问题所在

当我们用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