Capybara + Selenium-webdriver + фиксированные файлы RSpec + SSR, дающие Net :: ReadTimeout - PullRequest
0 голосов
/ 03 октября 2018

Я заметил странную проблему, которую я не мог решить в течение нескольких дней.

У меня есть сервер Rails 5 API с системными тестами, использующими RSpec и Capybara + Selenium-webdriver, которые ведут без головыХром.

Я использую 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

Это не удастся, возможно, в 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)>'

Я должен отметить, что это не всегда происходитс page.evaluate_script('window.location.reload()').Это может произойти с чем-то мягким, например visit('/').


Edit : я попытался отключить Ember FastBoot (рендеринг на стороне сервера) с помощью переменной DISABLE_FASTBOOT env и внезапно все тестыпроходить.Я думаю, что каким-то образом приборы RSpec в некоторых случаях приводят к тому, что Ember FastBoot не завершает рендеринг.Это, безусловно, соответствует сброшенным соединениям, которые я иногда видел в производственных журналах.

Я экспериментировал с клиентским кодом, и это может быть связано с моим использованием FastBoot's deferRendering call .


Редактировать : я использую следующие версии:

  • ember-cli: 3.1.3
  • ember-данные: 3.0.2
  • рельсы: 5.2.1
  • rspec: 3.8.0
  • капибара: 3.8.2
  • selenium-webdriver: 3.14.0
  • Google Chrome: 69.0.3497.100 (официальная сборка) (64-разрядная версия)

Редактировать : я использую это несколько странноУзел / Экспресс библиотека fastboot-app-server для выполнения рендеринга на стороне сервера.Я обнаружил, что иногда он удаляет важные заголовки ответа (Content-Type и Content-Encoding).Мне интересно, не способствует ли это этой проблеме.


Редактировать : я добавил строгую Политику безопасности контента, чтобы убедиться, что во время набора тестов не выполняются внешние запросы,может быть причиной Net::ReadTimeout.

Я проверяю вкладку сети Chrome в тот момент, когда она блокируется и кажется, что ничего не загружается.Обновление браузера вручную позволяет тестам забрать и продолжить работу.Как странно.

Я потратил пару недель на это сейчас, и, возможно, пришло время отказаться от тестов Selenium.

Я обновился до Chrome 70 и chromedriver 2.43.Похоже, что это не имеет значения.

Я попытался использовать гем rspec-retry для принудительного обновления, когда истекает время ожидания, но кажется, что драгоценному камню не удалось поймать исключение времени ожидания.

Я проверил сырой запрос к chromedriver, где вещи висят.Похоже, это всегда POST http://127.0.0.1/session/<session id>/refresh.Я попытался обновить по-другому: visit(page.current_path), который, кажется, все исправляет!

1 Ответ

0 голосов
/ 22 октября 2018

Я наконец получил свой набор тестов, чтобы переключить page.driver.browser.navigate.refresh на visit(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

Я пытался просто сделать visit(page.current_url), но это также давало тайм-ауты.

...