Capybara + Selenium-webdriver + RSpec文件夹具+ SSR给出Net :: ReadTimeout

Mar*_*ska 6 rspec ruby-on-rails capybara ember.js selenium-webdriver

我注意到一个我几天都无法解决的奇怪问题.

我有一个Rails 5 API服务器,系统测试使用RSpec和Capybara + Selenium-webdriver驱动无头Chrome.

我正在Capybara.app_host = 'http://localhost:4200'使测试成为一个运行Ember前端的独立开发服务器.Ember前端查看要知道的用户代理,然后将请求发送到Rails API测试数据库.

除了使用RSpec文件夹具的测试外,所有测试都运行良好.

这是一个失败的规范:

describe 'the affiliate program', :vcr, type: :system do
  fixtures :all

  before do
    Capybara.session_name = :affiliate
    visit('/')
    signup_and_verify_email(signup_intent: :seller)
    visit_affiliate_settings
  end

  it 'can use the affiliate page' do
    affiliate_token = page.text[/Your affiliate token is \b(.+?)\b/i, 1]
    expect(affiliate_token).to be_present

    # When a referral signs up.
    Capybara.session_name = :referral
    visit("?client=#{affiliate_token}")
    signup_and_verify_email(signup_intent: :member)

    refresh

    # It can track the referral.
    Capybara.session_name = :affiliate
    refresh
    expect(page).to have_selector('.referral-row', count: 1)

    # When a referral makes a purchase.
    Capybara.session_name = :referral
    find('[href="/videos"]').click
    find('.price-area .coin-usd-amount', match: :first).click
    find('.cart-dropdown-body .checkout-button').click
    find('.checkout-button').click
    wait_for { find('.countdown-timer') }
    order = Order.last
    order.force_complete_payment!
    Rake::Task['affiliate_referral:update_amounts_earned'].invoke

    # It can track the earnings.
    Capybara.session_name = :affiliate
    refresh
    amount = (order.price * AffiliateReferral::COMMISSION_PERCENTAGE).floor.to_f
    amount_in_dom = find('.referral-amount-earned', match: :first).text.gsub(/[^\d\.]/, '').to_f * 100
    expect(amount).to equal(amount_in_dom)
  end
end
Run Code Online (Sandbox Code Playgroud)

这可能会失败99%的时间.有一个奇怪的情况,它通过.我可以让我的测试套件最终通过循环运行一天来通过.

我最终将所有版本升级到最新版本(节点10,最新的Ember,最新的Rails),但问题仍然存在.

我可以发布一个样本仓库,以便稍后再现该问题.我只是想发布这个以防万一有人遇到过这个问题.

这是超时发生时的典型堆栈跟踪:

 1.1) Failure/Error: page.evaluate_script('window.location.reload()')

      Net::ReadTimeout:
        Net::ReadTimeout



      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:97:in `block in request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:110:in `block in request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/webmock-3.3.0/lib/webmock/http_lib_adapters/net_http.rb:109:in `request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/default.rb:121:in `response_for'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/default.rb:76:in `request'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/http/common.rb:62:in `call'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/bridge.rb:164:in `execute'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/oss/bridge.rb:584:in `execute'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/remote/oss/bridge.rb:267:in `execute_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/selenium-webdriver-3.14.0/lib/selenium/webdriver/common/driver.rb:211:in `execute_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/selenium/driver.rb:84:in `execute_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/selenium/driver.rb:88:in `evaluate_script'
      # /home/mhluska/.rvm/gems/ruby-2.5.1/gems/capybara-3.8.2/lib/capybara/session.rb:575:in `evaluate_script'
      # ./spec/support/selenium.rb:48:in `refresh'
      # ./spec/support/pages.rb:70:in `signup_and_verify_email'
      # ./spec/system/payment_spec.rb:43:in `block (3 levels) in <top (required)>'
Run Code Online (Sandbox Code Playgroud)

我应该指出它并不总是会发生page.evaluate_script('window.location.reload()').它可能发生在一些良性的事情上visit('/').


编辑:我尝试使用DISABLE_FASTBOOTenv变量禁用Ember FastBoot(服务器端渲染),突然所有测试都通过.我想在某种情况下,RSpec灯具会导致Ember FastBoot无法完成渲染.这肯定与我在生产日志中偶尔看到的连接断开有关.

我一直在试验客户端代码,这可能是由于我使用了FastBoot的deferRendering调用.


编辑:我使用以下版本:

  • ember-cli:3.1.3
  • 余烬数据:3.0.2
  • 铁轨:5.2.1
  • rspec:3.8.0
  • 水豚:3.8.2
  • selenium-webdriver:3.14.0
  • 谷歌浏览器:69.0.3497.100(官方版)(64位)

编辑:我正在使用这个有点不稳定的Node/Express库fastboot-app-server来进行服务器端渲染.我发现它有时会删除重要的响应头(Content-Type和Content-Encoding).我想知道这是否有助于解决这个问题.


编辑:我添加了严格的内容安全策略,以确保在测试套件中没有可能导致的外部请求Net::ReadTimeout.

我检查Chrome网络选项卡,当它锁定时,似乎什么都没有加载.手动刷新浏览器允许测试接收并继续运行.多么奇怪.

我现在花了几个星期的时间,可能是时候放弃Selenium测试了.

我升级到Chrome 70和chromedriver 2.43.它似乎没有什么区别.

我尝试使用rspec-retry gem在超时发生时强制刷新但是gem似乎无法捕获超时异常.

我已经检查过对chromedriver的原始请求.它看起来总是如此POST http://127.0.0.1/session/<session id>/refresh.我尝试以另一种方式刷新:visit(page.current_path)这似乎解决了问题!

Mar*_*ska 0

我终于通过切换page.driver.browser.navigate.refreshvisit(page.current_path).

我知道这是一个丑陋的黑客,但这是我能找到的唯一能让事情正常工作的方法(请参阅我在问题编辑中的各种尝试)。

我查看了每次导致超时的 chromedriver 请求:POST http://127.0.0.1/session/<session id>/refresh。我只能猜测这是 chromedriver 的某种问题。也许顺便说一句,它仅在多个 chromedriver 实例处于活动状态时才会挂起(当使用多个 Capybara 会话时会发生这种情况)。

编辑:我还需要考虑查询参数:

def refresh   
  query = URI.parse(page.current_url).query
  path = page.current_path
  path += "?#{query}" if query.present?

  visit(path)
end
Run Code Online (Sandbox Code Playgroud)

我尝试这样做,visit(page.current_url)但这也导致超时。