使用 AJAX 请求在 Cucumber 中自动加载相关的竞争条件

Posted

技术标签:

【中文标题】使用 AJAX 请求在 Cucumber 中自动加载相关的竞争条件【英文标题】:Autoload related race condition in Cucumber with AJAX requests 【发布时间】:2014-09-13 22:39:11 【问题描述】:

我将 Cucumber 与 capybara-webkit 一起用于我的应用程序在 Ruby 2.0.0、Rails 4.1 上的集成测试。我的黄瓜测试套件中的一些测试意外地开始吐出如下错误:

  Circular dependency detected while autoloading constant UiValidators::ParameterFinder (RuntimeError)
  /Users/kingp/.rvm/gems/ruby-2.0.0-p451@triquest/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:484:in `load_missing_constant'
  /Users/kingp/.rvm/gems/ruby-2.0.0-p451@triquest/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:180:in `const_missing'
  /Users/kingp/Projects/rails-triquest/app/controllers/contacts_controller.rb:2:in `<class:ContactsController>'
  /Users/kingp/Projects/rails-triquest/app/controllers/contacts_controller.rb:1:in `<top (required)>'
  /Users/kingp/.rvm/gems/ruby-2.0.0-p451@triquest/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:247:in `require'
  ...

错误说是“循环依赖”,但它实际上是在 Rails 自动加载器尝试加载一个已经在其加载常量集中的常量时抛出的。通常这确实是由于循环依赖,但我很确定我的应用程序不是这种情况。带有崩溃测试的分支和我从中分叉的稳定分支之间的差异表明,唯一的变化是对咖啡脚本文件、视图模板、迁移和我正在编写的新黄瓜功能。我没有接触任何控制器或模型代码。

我最终在 Rails 自动加载器中插入了一些日志记录代码,以帮助我弄清楚发生了什么:

  # Inserted at activesupport-4.1.1/lib/active_support/dependencies.rb:467
  _thread_id_for_debug = Thread.current.object_id
  STDERR.puts "*** #loaded.count #from_mod #const_name - #_thread_id_for_debug"

loaded 是一组自动加载代码文件的路径,from_mod 是请求来自的上下文,const_name 是我们试图加载的常量。这一切最终让我在崩溃之前得到了这个:

  *** 104 Object SitesController - 70180261360940
  *** 105 Object ContactsController - 70180240113760
  *** 105 SitesController UiValidators - 70180261360940
  *** 105 Object UiValidators - 70180261360940
  *** 105 UiValidators ParameterFinder - 70180261360940
  *** 107 UiValidators ParameterFinder - 70180240113760 

看起来两个线程正在尝试自动加载相同的常量。我的猜测是常量的名称在第一个线程完成加载之前被添加到 Rails 的“加载”常量集中。第二个线程无法解析常量(因为加载尚未完成),要求自动加载器找到它,当自动加载器在其“已加载”集中看到常量时会引发。

在测试的这一点上,两个控制器(SitesController 和 ContactsController)正在响应 AJAX 请求,几乎同时启动。

我找到了一种解决崩溃的方法,只需在 AJAX 之前包含对模块 UiValidators::ParameterFinder 的引用。但这似乎很脆弱,也不是很优雅。除了开启测试环境的Eager Loading,有没有其他方法可以避免这个问题?

【问题讨论】:

我遇到了同样的问题。打开 eager_load 甚至没有为我修复它.. 不明白为什么不这样做,因为它不应该在启用该功能的情况下尝试自动加载?仍在调试.. 【参考方案1】:

我遇到了同样的问题(没有 Cucumber,只有 Capybara 和 Poltergeist)。设置config.eager_load = true 甚至对我都不起作用(不太明白为什么不……)。

我最终使用了Spring 并且从那以后没有出现循环依赖错误。

【讨论】:

【参考方案2】:

在使用 Sidekiq 时,Rails 4.1.4 也有同样的问题。我假设当调用 active_support 内的const_missing 时,线程化的 Sidekiq 工作人员内部的竞争条件会导致各种问题。

除了确保我当前的环境将执行即时加载,即通过config.eager_load = true,我还必须将我的工作人员正在使用的所有组件从lib 目录添加到config.eager_load_paths(通过config.eager_load_paths += %W(#config.root/lib) 内@ 987654326@).

这是必要的,因为我假设设置 config.eager_load = true 只会使 Rails 急切加载 app/ 目录的内容。

App::Application.config.eager_load_paths
=> [
  [0] "/home/archive/releases/20140721180504/app/assets",
  [1] "/home/archive/releases/20140721180504/app/controllers",
  [2] "/home/archive/releases/20140721180504/app/helpers",
  [3] "/home/archive/releases/20140721180504/app/mailers",
  [4] "/home/archive/releases/20140721180504/app/models",
  [5] "/home/archive/releases/20140721180504/app/services",
  [6] "/home/archive/releases/20140721180504/app/workers"
]

两者的结合似乎有助于解决这个问题。

【讨论】:

以上是关于使用 AJAX 请求在 Cucumber 中自动加载相关的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

Rails Cucumber使用Capybara测试AJAX

通过简单的AJAX递归请求进行多任务的数据自动处理

如何使用cucumber搭建ui自动化框架

如何使用 Frank Cucumber 自动进行滑动/滚动手势?

在ajax请求后台时在请求标头RequestHeader加token

ajax请求Url加参数的使用方法