为什么在挂钩后添加“sleep 1”会导致此Rspec / Capybara测试通过?

时间:2014-05-09 00:32:27

标签: ruby-on-rails ruby rspec capybara capybara-webkit

我使用的是rails 4.0.5,rspec 2.14.1,capybara 2.2.1,capybara-webkit 1.1.0和database_cleaner 1.2.0。我通过以下功能测试看到一些奇怪的行为(模拟用户在帖子上查看评论,将鼠标悬停在图标上以显示菜单,然后单击菜单项以删除评论):

let(:user){create(:user)}
let(:post){create(:post, author: user)}
let!(:comment){create(:comment, post: post, author: user)}

...

it "can delete a comment" do
  assert(page.has_css? "#comment-#{comment.id}")
  find("#comment-#{comment.id}-controls").trigger(:mouseover)
  find("#comment-#{comment.id} .comment-delete a").click
  assert(page.has_no_css? "#comment-#{comment.id}")
end

此测试大约80%的时间都失败,总是由于某些记录从数据库中检索为nil - 我得到NoMethodError: undefined method X for nil:NilClass,因为X的各种值。有时nil是评论被删除,有时它是评论所附的帖子,有时它是评论/帖子的作者。

如果我在测试结束时添加sleep 1,则会通过:

it "can delete its own comment" do
  assert(page.has_css? "#comment-#{comment.id}")
  find("#comment-#{comment.id}-controls").trigger(:mouseover)
  find("#comment-#{comment.id} .comment-delete a").click
  assert(page.has_no_css? "#comment-#{comment.id}")
  sleep 1
end

如果我将sleep 1放在after块中,它也会通过。

我知道为什么我得到这些NoMethodErrors,和/或为什么测试通过,如果我在完成所有工作后让它睡了一秒钟?

2 个答案:

答案 0 :(得分:5)

我怀疑在您从数据库中删除评论之前,评论可能会从页面中消失(这是您最后要断言的内容)。这意味着测试可以在删除发生之前进行清理。如果是这种情况,您可以通过等待在测试结束时发生实际删除来修复它。我有这种方法(重新实现a method that was removed from Capybara 2 but is still sometimes necessary

def wait_until(delay = 1)
  seconds_waited = 0
  while ! yield && seconds_waited < Capybara.default_wait_time
    sleep delay
    seconds_waited += 1
  end
  raise "Waited for #{Capybara.default_wait_time} seconds but condition did not become true" unless yield
end

所以我可以做到

wait_until { Comment.count == 0 }

在测试中。

另一种方法是添加Rack中间件,以阻止测试结束后发出的请求。这里详细描述了这种方法:http://www.salsify.com/blog/tearing-capybara-ajax-tests我看到它在解决大规模RSpec特性规范中的数据泄漏方面做得很好。

答案 1 :(得分:-1)

https://github.com/jnicklas/capybara#asynchronous-javascript-ajax-and-friends

  

使用异步JavaScript时,您可能会遇到   您尝试与元素交互的情况   尚未出现在页面上。 Capybara自动处理此事   等待元素出现在页面上。