【问题标题】:Capybara integration testing with asynchronous JavaScriptCapybara 使用异步 JavaScript 进行集成测试
【发布时间】:2012-09-23 20:08:52
【问题描述】:

我有一个失败的 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 并在那里找到链接?如果是这样,我不知道如何修复此测试以确保测试通过。刷新页面可以解决问题,但这并不是用户会做的事情,所以我犹豫是否更改页面只是为了让测试通过......

希望有任何关于如何解决这个问题的建议。

谢谢!

【问题讨论】:

  • 您终于找到解决问题的方法了吗?我有一个类似的,我想知道你是否可以帮助我。
  • 不,很遗憾没有。我花了太多时间让那个单一的测试工作,所以我最终只是把它注释掉而不是修复它。抱歉,我无法提供更多帮助……在花了这么多时间之后,我没有精力深入研究!如果您弄明白了,请随时回答这个问题,我很乐意为您效劳!
  • 您是否记得在您的 rspec 中包含 js: true

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


【解决方案1】:

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")

【讨论】:

  • 不使用jquery怎么办?
【解决方案2】:

有一些方法叫做wait_until,但最近被弃用了,改用同步方法。

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

暂时不知道具体怎么用,等待作者解答,希望能尽快解决这个问题

【讨论】:

    【解决方案3】:

    我从this article 学到了一种检查ajax 请求是否完成的好方法。您可以使用 ajax $.active 函数(即not in the actual API but is exposed,以便您可以使用它),而不是使用特定时间的wait$.active 告诉您与服务器的活动连接数,因此当它降至零时,您就知道 ajax 请求已完成:

    wait_until do
      page.evaluate_script('$.active') == 0
    end
    

    如果这不起作用,那么问题出在其他地方(从您写的内容来看,这很可能)。如果更改只发生在 DOM 中,那么您必须确保为您的测试/规范启用了 javascript。例如,在 rspec 中,您设置 :js => true 来执行此操作;在 Cucumber 中,您在场景上方添加一行 @javascript。我不使用 rails 的默认测试,但必须有一个设置来做同样的事情。

    【讨论】:

    • 嗯...我在 Capybara 下使用 Selenium 驱动程序,默认情况下它似乎支持 JavaScript 测试(请参阅github.com/jnicklas/capybara#drivers)。我尝试添加您的等待代码,但这并没有解决问题。不过好主意!只是还没解决……
    • 您是否使用 capybara 进行任何其他涉及 javascript 的测试?
    • 是的,但这是唯一一个正在寻找已删除信息的人。所有其他的东西似乎都可以工作......例如,检查 AJAX 查询是否会导致新按钮或更改的内容。但是关于 jQuery remove() 的一些东西似乎并不好。如果我检查page.html,它仍然会在那里显示内容,所以这里肯定有问题。
    • 哦,好吧,那是另一个问题。
    • 这似乎不适用于当前版本的水豚/poltergeist:Failure/Error: wait_until do NoMethodError: undefined method `wait_until' for #<:core::examplegroup::nested_30:>
    【解决方案4】:

    您是先用链接测试其他东西,然后再测试它是否被删除?

    换句话说,您的测试是否类似于:

    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 中再次查询该链接以查看是否得到结果。

    【讨论】:

      【解决方案5】:

      我必须重写 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
      

      【讨论】:

      • 对于 Capybara 的更新版本,请注意 default_wait_time 已被弃用。立即使用default_max_wait_time
      【解决方案6】:

      我想到的几种方法:

      1:检查您的水豚版本并查找您的版本的任何错误

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

      click_button("删除东西") find(:xpath, '//a[text()='链接应该被移除').should be_false

      3: 使用 has_link?而不是 has_no_link?

      click_button("删除东西") page.has_link?("链接应该被移除").should be_false

      【讨论】:

        【解决方案7】:

        您始终可以手动执行此操作。使用@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
        

        【讨论】:

          猜你喜欢
          • 2014-04-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-28
          • 2017-02-07
          • 1970-01-01
          • 2017-05-18
          相关资源
          最近更新 更多