我的 Rails 集成测试失败了,我不明白为什么。我使用 Capybara 和 Selenium 作为驱动程序。
该测试检查 AJAX 调用发生后页面内容是否已被“删除”。相关操作是单击一个按钮,单击该按钮会导致通过 jQuery remove()
调用删除页面的一部分。这是集成测试代码的近似值:
click_button("Remove stuff")
assert has_no_link?("This should be removed")
断言失败,意味着链接仍然存在。
我一直在阅读有关 Capybara 的内容,我知道您可以延长默认等待时间。我已将其延长到一个荒谬的值(20 秒),但断言仍然失败。
当我手动遵循测试过程时,页面的
source仍然显示内容,但 DOM 不显示(通过查看 Firefox 的 DOM Inspector 并查找元素)。这是问题吗?我什至尝试在 Firefox 中运行测试时检查 DOM,以检查内容是否存在,但似乎并不存在。 我不知道 Capybara 是如何找到这个 DOM 中不再存在的链接的。 Capybara 是否检查源代码而不是 DOM 并找到那里的链接?如果是这样,我不知道如何修复此测试以确保测试通过。刷新页面可以解决问题,但这并不完全是用户会做的,所以我犹豫是否要更改页面只是为了让测试通过...... 希望获得有关如何解决此问题的任何建议。
Thoughtbot 有一篇关于等待 AJAX 的精彩博客文章,您可以在
here它非常适合水豚等待时间不够长的情况,但不会增加不必要的长时间超时。我现在主要在 Rspec 中工作,但我认为你可以通过这样做来修改它:
# Create this file in test/support/wait_for_ajax.rb
module WaitForAjax
def wait_for_ajax
Timeout.timeout(Capybara.default_max_wait_time) do
loop until finished_all_ajax_requests?
end
end
def finished_all_ajax_requests?
page.evaluate_script('jQuery.active').zero?
end
end
您可以在需要时将其包含在单独的测试文件中,或者使用
this SO post中提供的策略之一来每次自动包含它。
然后,每当您的测试未正确等待 AJAX 完成时,只需插入行 wait_for_ajax
。以您的代码为例:
click_button("Remove stuff")
wait_for_ajax
assert has_no_link?("This should be removed")
有一个名为 wait_until 的方法,但最近已弃用并用同步方法更改。
https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L44
目前我还不知道具体如何使用它,但我正在等待作者对我的问题的回答,所以我希望尽快解决这个问题
有一种巧妙的方法可以检查 ajax 请求是否已完成,这是我从
这篇文章wait
函数(该函数不在实际 API 中,但已公开,因此您可以使用它),而不是具有特定时间的
$.active
。 $.active
告诉您与服务器的活动连接数,因此当它降至零时,您就知道 ajax 请求已完成:
wait_until do
page.evaluate_script('$.active') == 0
end
如果这不起作用,则问题出在其他地方(从您所写的内容来看,这似乎很可能)。如果更改仅发生在 DOM 中,那么您必须确保为您的测试/规范启用了 javascript。例如,在 rspec 中,您可以设置
:js => true
来做到这一点;在 Cucumber 中,您可以在场景上方添加一行
@javascript
。我不使用 Rails 的默认测试,但必须有一个设置来执行相同的操作。
您是否首先使用该链接测试其他内容,然后测试它是否已被删除?
has_no_link = $('#id_for_link')
//.... some test
click_button("Remove stuff")
assert has_no_link?("This should be removed")
如果是这种情况,那么 has_no_link 仍然会指向该链接。 remove() 会将其从 DOM 中删除,但您的变量仍然在内存中指向它。
您应该在 DOM 中再次查询该链接,看看是否得到结果。
我必须重写 wait_until 进行测试,该测试涉及等待通过将 YouTube 视频流式传输到某个点而触发的回调。这是我用过的:
我想到的几种方法:
2:也许尝试在单击按钮后在链接上进行查找
click_button("删除内容") find(:xpath, '//a[text()='链接应被删除').should be_false
3:使用has_link?而不是 has_no_link?
click_button("删除内容") page.has_link?("链接应该被删除").should be_false
您始终可以手动执行此操作。使用@shioyama的想法: