什么是调试间歇性失败规范的系统方法?

时间:2016-05-09 11:05:32

标签: ruby-on-rails ajax rspec capybara

我的Capybara / Rspec套件中有四个测试失败(CI部署的真正问题)。

最糟糕的是,这些测试间歇性地失败,并且通常仅在整个套件运行时才会进行调试。

它们都是ajax请求,要么提交远程表单,要么单击远程链接,然后是expect(page).to have_content 'My Flash Message'

这些测试甚至在同一测试周期内间歇性地失败。例如,我有几个行为相似的模型,所以我正在迭代它们进行测试。

e.g., 
['Country', 'State', 'City'].each do |object|
  let(:target) { create object.to_sym }
  it 'runs my frustrating test' do 
  end
end

有时国家失败,有时国家,有时一切都过去了。

我尝试将wait: 30添加到expect语句中。我尝试在expect语句之前添加sleep 30。我还在接受间歇性的传球。

有相当多的信息描述了挑剔的ajax测试,但我还没有找到很多关于如何调试和修复这些问题的信息。

在我拔掉头发之前,我真的很感激任何人的建议或指示!

更新

感谢所有这些出色的回复。看到其他人已经解决了类似问题并且我并不孤单,这很有用。

那么,有解决方案吗?

使用调试工具的建议,如pry,byebug,Poltergeist的调试功能(感谢@ Jay-Ar Polidario,@ TomWalpole),有助于确认我认为我已经知道的东西 - 即,并且正如@ BM5K所建议的那样这些功能在浏览器中始终如一,错误在于测试。

我尝试调整超时和重试(@ Jay-Ar Polidario,@ BM5K),虽然这些改进仍然不是一致的修复。更重要的是,这种方法感觉就像修补孔而不是正确的修复,所以我并不完全舒服。

最终,我对这些测试进行了重大改写。这需要分解多步功能,并单独设置和测试每个步骤。虽然纯粹主义者可能声称这不是从用户的角度进行真正的测试,但每次测试之间都有足够的重叠,我对结果感到满意。

在完成这个过程中,我注意到所有这些错误都与“点击事物或填写表格”有关,正如@BoraMa建议的那样。虽然在这种情况下经验被颠倒了 - 我们采用了.trigger('click')语法,因为capybara + poltergeist报告使用click_linkfind(object).click点击元素时出错,而且这些测试都存在问题。

为了避免这些问题,我尽可能地从测试中删除了JS。即,在没有启用JS的情况下测试大部分功能,然后创建非常短的,有针对性的JS规范来测试特定的JS响应,功能或用户反馈。

所以没有一个单独的修复。一种重要的重构,说实话,可能需要发生并且是一项有价值的练习。通过将所有内容分解为单独的测试,测试已经失去了一些功能,但总体而言,这使得测试更容易阅读和维护。

仍有一些测试偶尔显示为红色,需要更多工作。但整体上有很大改进。

感谢大家的指导,并向我保证测试环境中的交互可能是根本原因。

3 个答案:

答案 0 :(得分:4)

间歇性的测试失败是一个很难排除的问题,但是你可以采取一些措施来简化生活。首先是删除任何循环或共享示例。明确说明每个期望应该使得更清楚哪个示例组合失败(或者更明显它确实是随机的)。

在多次运行过程中,跟踪哪些测试失败。它们都在同一个上下文组中吗?

您是在混合和匹配javascript测试和非JavaScript测试吗?如果你是,你可能会遇到数据库问题(我已经看到在上下文块中切换数据库清理策略引起的问题)。

确保考虑测试所在的任何父上下文块。

如果这些都没有缩小您的搜索范围,请使用允许您重试失败测试的gem。

我过去曾使用respec-retry,但最近发现它不可靠。我已切换到rspec-repeat。我通常将这些保留在开发中(配置为1次尝试)并在CI上进行多次尝试(通常为3次)。通过这种方式,我可以了解哪些测试在本地抖动,但不要让这些测试破坏我的构建(除非它们始终失败)。

TL; DR

我遇到的大多数间歇性失败的测试都有很多移动部件(导轨,水豚,数据库清洁工,工厂女孩,幻影,rspec等等)。如果代码经过测试并且规范经常通过并且该功能在浏览器中始终有效,则测试环境中的某些交互可能是间歇性故障的根本原因。如果您无法跟踪该情况,请多次重试失败的规范。

答案 1 :(得分:4)

让我带出故事:)。最近,我们还试图在类似的设置(Poltergeist,JS测试)中间歇性地进行失败的测试来寻找并修复问题。当整个测试套件运行时,测试失败的可能性大于单独但在整个套件成功的大约三分之一时间。这只是套件中的几个测试,大约10个,随机失败,其他人似乎一直运行正常。

首先,我们确保测试没有失败,因为数据库截断问题,剩余记录等。我们在失败的时候制作了截图,以验证页面看起来是否正确。

经过大量搜索后,我们注意到所有剩余的失败测试都涉及点击事物或填写表单,同时还有jQuery动画和页面上经常使用的其他动态操作。这引导我们看到Poltergeist issue这最终帮助了我们。事实证明,当点击按钮或处理表单输入时,Poltergeist会尝试最大限度地模仿普通用户,这会在输入/链接动画时导致问题。

一种认识到这对我们来说确实是一个问题的方法是我们可以成功find页面上的元素,但浏览器无法点击它。

我们最终使用了一个不太干净的解决方案 - 我们重写了一些水豚帮手,用于点击并与表单进行互动,以便在内部使用findtrigger

# override capybara methods as they react badly with animations 
# (click/action is not registered then and test fails)
# see https://github.com/teampoltergeist/poltergeist/issues/530
def click_button(locator, *options)
  find_button(locator, *options).trigger(:click)
end

def click_link(locator, *options)
  find_link(locator, *options).trigger(:click)
end

def choose(locator, *options)
  find(:radio_button, locator, *options).trigger(:click)
end

def check(locator, *options)
  find(:checkbox, locator, *options).trigger(:click)
end

这种方法可能会导致一些意想不到的问题,因为现在您可以点击测试中的内容,即使它们是例如由模态div重叠或在页面上不完全可见时。但在仔细阅读了关于github问题的评论后,我们认为这是我们的方法。

从那时起,我们只偶尔会出现一些测试失败,这些失败似乎与另一位Poltergeist timeouts issue有关。但是失败是如此罕见,以至于我们感觉不到进一步观察的冲动 - 测试最终足够可靠。

答案 2 :(得分:1)

如果您确定服务器(Rails)和客户端(JS)都没有更改变量。如果可行,您可以尝试以下操作。我们用它来解决我们遇到的类似问题。

规格/支持/ wait_for_ajax.rb

# ref: https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara
module WaitForAjax
  def wait_for_ajax
    Timeout.timeout(Capybara.default_max_wait_time) do
      loop until finished_all_ajax_requests?
    end
    sleep(1) # ensure just because above doesn't always work
  end

  def finished_all_ajax_requests?
    page.evaluate_script('jQuery.active').zero?
  end
end

规格/特征/ YOUR_SPEC.rb

Rspec.feature 'My Feature Test', type: :feature do
  ['Country', 'State', 'City'].each do |object|
    let(:target) { create object.to_sym }
    it 'runs my frustrating test' do 
      find('#my-div').click
      wait_for_ajax
    end
  end
end

rails_helper.rb

# ..
RSpec.configure do |config|
  # ..
  config.include WaitForAjax, type: :feature
  # ..
end
# ..