使用异步JavaScript进行Capybara集成测试

时间:2012-09-23 20:08:53

标签: javascript jquery ruby-on-rails selenium capybara

我有一个失败的Rails集成测试,我无法弄清楚原因。我正在使用Capybara和Selenium作为驱动程序。

在进行AJAX调用后,测试会检查页面内容是否已被删除。相关操作是单击一个按钮,单击该按钮会导致页面的一部分通过jQuery remove()调用删除。以下是集成测试代码的近似值:

click_button("Remove stuff")
assert has_no_link?("This should be removed")

断言失败,暗示链接仍然存在。

我一直在阅读Capybara,我知道你可以延长默认的等待时间。我把它扩展到一个荒谬的值(20秒),但断言仍然失败。

当我自己手动关注测试过程时,页面的仍显示内容,但 DOM 不显示(通过查看Firefox的DOM Inspector并查找元素)。这是问题吗?我甚至尝试检查DOM ,而测试在Firefox中运行,以检查内容是否存在,并且看起来不是。

我不知道Capybara如何在DOM中找到不再存在的链接。 Capybara是在检查源而不是DOM并在那里找到链接吗?如果是这样,我不知道如何修复此测试以确保测试通过。刷新页面可以解决问题,但这不完全是用户会做的事情,所以我犹豫改变页面只是为了让测试通过......

会喜欢有关如何解决此问题的任何建议。

谢谢!

7 个答案:

答案 0 :(得分:3)

Thoughtbot有关于等待AJAX​​的精彩博文,您可以阅读here,虽然它基于Rspec,看起来您正在使用TestUnit。

它非常适用于Capybara不太久等待的情况,但不会增加不必要的长时间超时。我现在主要在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")

答案 1 :(得分:1)

有一些名为wait_until的方法,但最近已弃用,并使用synchronize方法进行了更改。

http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara

https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L44

目前我不知道如何使用它,但我正在等待作者回答我的问题,所以我希望尽快解决这个问题

答案 2 :(得分:0)

有一种巧妙的方法来检查ajax请求是否已完成,这是我从this article学到的。您可以使用ajax wait函数(not in the actual API but is exposed来代替$.active,而不是$.active,因此您可以使用它。 wait_until do page.evaluate_script('$.active') == 0 end 告诉您与服务器的活动连接数,因此当它降至零时,您就知道ajax请求已完成:

:js => true

如果这不起作用,那么问题就出在其他地方(根据你所写的内容来判断)。如果更改仅发生在DOM中,那么您必须确保为您的测试/规范启用了javascript。例如,在rspec中,您设置@javascript来执行此操作;在Cucumber中,您可以使用{{1}}在方案上方添加一行。我不使用rails的默认测试,但必须有一个设置来做同样的事情。

答案 3 :(得分:0)

您是否首先使用该链接测试其他内容,然后测试是否已将其删除?

换句话说,你的测试是这样的:

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中查询链接,看看是否得到了结果。

答案 4 :(得分:0)

我不得不重写wait_until进行测试,该测试涉及等待通过将YouTube视频流式传输到特定点而触发的回调。这是我使用的:

# in spec_helper.rb
require "timeout"
def wait_until time=0.1
    Timeout.timeout(Capybara.default_wait_time) do
        sleep(time) until value = yield
        value
    end
end

答案 5 :(得分:0)

我想到了几种方法:

1:检查您的水豚版本并查找您的版本

的任何错误

2:也许在单击按钮后尝试在链接上进行查找

click_button(“删除内容”) find(:xpath,'// a [text()='Link应该删除')。应该be_false

3:使用has_link?而不是has_no_link?

click_button(“删除内容”) page.has_link?(“应该删除链接”)。应该是be_false

答案 6 :(得分:0)

您可以随时手动执行此操作。使用@ shioyama的想法:

  def wait_for_ajax
    timer_end = Time.now + 5.seconds 
    while page.evaluate_script('$.active') != 0    
      if Time.now > timer_end
        fail "Page took more than 5 seconds to load via ajax"
      end 
      sleep 0.1
    end
  end
相关问题