You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Ruby与Heroku至20版本后CircleCI测试失败求助

排查CircleCI上Capybara/Selenium测试失败问题

问题背景

维护一个后端基于Ruby on Rails、前端基于React.js的应用,升级Heroku 18到Heroku 20并更新依赖后,本地测试全部正常,但CircleCI上有3个依赖selenium-webdriver、webdrivers、capybara的测试持续失败,错误均为无法找到可见的user_email字段。已确认字段ID正确,且其他使用同一login_as方法的测试无异常,延长Capybara超时时间后问题依旧。

错误信息

Failures:

  1) Posts create post
     Failure/Error: fill_in :user_email, with: user.email
     
     Capybara::ElementNotFound:
       Unable to find visible field :user_email that is not disabled
     # ./spec/support/auth_helper.rb:5:in `login_as'
     # ./spec/features/beneficiary/posts/post_spec.rb:7:in `block (2 levels) in <main>'
     # ./spec/rails_helper.rb:46:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:45:in `block (2 levels) in <top (required)>'

  2) donor dashboard should have nav elements
     Failure/Error: fill_in :user_email, with: user.email
     
     Capybara::ElementNotFound:
       Unable to find visible field :user_email that is not disabled
     # ./spec/support/auth_helper.rb:5:in `login_as'
     # ./spec/features/donor/dashboard_spec.rb:7:in `block (2 levels) in <main>'
     # ./spec/rails_helper.rb:46:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:45:in `block (2 levels) in <top (required)>'

  3) Posts create post
     Failure/Error: fill_in :user_email, with: user.email
     
     Capybara::ElementNotFound:
       Unable to find visible field :user_email that is not disabled
     # ./spec/support/auth_helper.rb:5:in `login_as'
     # ./spec/features/donor/posts/post_spec.rb:7:in `block (2 levels) in <main>'
     # ./spec/rails_helper.rb:46:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:45:in `block (2 levels) in <top (required)>'

相关代码

auth_helper.rb

def login_as(user, password: "password")
  visit root_path

  find('a', text: 'Login').click
  fill_in :user_email, with: user.email
  fill_in :user_password, with: password
  find("input[value='Log in']").click
end

rails_helper.rb(相关配置)

config.around(:each) do |example|
  DatabaseCleaner.cleaning do
    example.run
  end
end

CircleCI config.yml

# CircleCI 2.0 configuration file
version: 2.1

orbs:
  browser-tools: circleci/browser-tools@1.4.0

jobs:

  build:
  
    docker:
      # specify the version you desire here
       - image: cimg/ruby:3.0.5-browsers # circleci/ruby:2.7.0-node-browsers 
         environment:
          PGHOST: 127.0.0.1
          PGUSER: $HEROKU_APP_NAME
          RAILS_ENV: test
          BUNDLER_VERSION: 2.0.2
      # Specify service dependencies here if necessary
      # CircleCI maintains a library of pre-built images
       - image: circleci/postgres:9.5-alpine
         environment:
          POSTGRES_USER: $HEROKU_APP_NAME
          POSTGRES_DB: $HEROKU_APP_NAME_test
          POSTGRES_PASSWORD: $HEROKU_APP_NAME
       - image: redis:4.0.10

    working_directory: ~/repo

    steps:
      - checkout
      - browser-tools/install-browser-tools

      # Download and cache dependencies
      - restore_cache:
          keys:
          - v1-dependencies-{{ checksum "Gemfile.lock" }}
          # fallback to using the latest cache if no exact match is found
          - v1-dependencies-

      - run:
          name: Swap node versions
          command: |
            set +e
            wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash
            export NVM_DIR="$HOME/.nvm"
            [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
            [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
            nvm install v14
            nvm alias default 14.21.1

            echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
            echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV

      - run:
          name: Configure Bundler
          command: |
            echo 'export BUNDLER_VERSION=$(cat Gemfile.lock | tail -1 | tr -d " ")' >> $BASH_ENV
            source $BASH_ENV
            gem install bundler

      - run:
          name: install dependencies
          command: |
            bundle config set --local path 'vendor/bundle'
            bundle install --jobs=4 --retry=3 

      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}

      # Database setup
      - run: bundle exec rails db:create
      - run: bundle exec rails db:schema:load

      # frontend
      - restore_cache:
          keys:
            - sell-more-yarn-cache-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - sell-more-yarn-cache-{{ .Branch }}-
            - sell-more-yarn-cache-
     
      - run:
          name: Yarn Install
          command: |
            yarn --ignore-engines --cache-folder ~/.cache/yarn

      - save_cache:
          key: sell-more-yarn-cache-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - ~/.cache/yarn
      
      - run:
          name: Webpacker Pre-Compile
          command: bundle exec rails webpacker:compile

      # run tests!
      - run:
          name: run tests
          command: |
            bundle exec rspec --profile 10 \
              --out test_results/rspec.xml \
              --format progress \
              $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)

      # collect reports
      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results

workflows:
  version: 2
  build-and-deploy:
    jobs:
      - build

Gemfile版本信息

ruby '3.0.5'
gem 'rails', '~> 6.1'

group :test do
  gem 'capybara', '~> 3.38'
  gem 'database_cleaner'
  gem 'factory_bot_rails', '~> 6.2.0'
  gem 'rack_session_access'
  gem 'rails-controller-testing'
  gem 'rspec-rails'
  gem 'selenium-webdriver', '~> 4.0'
  gem 'shoulda-callback-matchers', '~> 1.1.1'
  gem 'shoulda-matchers', '~> 4.4'
  gem 'spring-commands-rspec'
  gem 'stripe-ruby-mock', github: 'rebelidealist/stripe-ruby-mock'
  gem 'timecop'
  gem 'webdrivers', '~> 5.2'
  gem 'webmock'
end

排查方向建议

  • 调整CI环境浏览器窗口尺寸:CircleCI无头浏览器默认窗口可能过小,导致响应式布局下登录表单被隐藏(比如移动端菜单折叠)。在login_as方法中添加窗口大小设置:

    page.driver.browser.manage.window.resize_to(1280, 1024)
    

    或者在rails_helper里全局配置Capybara的窗口大小。

  • 捕获CI环境页面截图:在fill_in前添加截图代码,将截图作为CircleCI artifact存储,直观查看页面实际状态:

    page.save_screenshot('login_page.png')
    

    然后在CircleCI配置的store_artifacts步骤中添加截图路径,确认登录表单是否正确渲染。

  • 强制清理Webpack缓存:CI环境下Webpack编译可能存在缓存问题,导致React组件未正确渲染。修改Webpack编译命令为:

    bundle exec rails webpacker:clobber && bundle exec rails webpacker:compile
    
  • 更新测试依赖版本:当前使用的selenium-webdriver(4.0)和`webdrivers`(5.2)可能与CI环境的浏览器版本不兼容,尝试升级到最新稳定版:

    gem 'selenium-webdriver', '~> 4.10'
    gem 'webdrivers', '~> 5.4'
    
  • 优化无头浏览器配置:确认Capybara使用最新的无头Chrome参数,避免旧参数导致渲染异常,在rails_helper中添加:

    Capybara.register_driver :selenium_chrome_headless do |app|
      options = Selenium::WebDriver::Chrome::Options.new
      options.add_argument('--headless=new')
      options.add_argument('--disable-gpu')
      options.add_argument('--no-sandbox')
      options.add_argument('--window-size=1280,1024')
      Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
    end
    Capybara.default_driver = :selenium_chrome_headless
    Capybara.javascript_driver = :selenium_chrome_headless
    
  • 精准定位Login按钮:避免文本匹配的不确定性,改用CSS选择器或ID定位Login按钮,比如:

    find('#login-link').click # 假设按钮有ID login-link
    
  • 添加显式等待逻辑:在点击Login按钮后,显式等待登录表单字段出现,确保页面完全加载:

    find('a', text: 'Login').click
    expect(page).to have_field(:user_email, visible: true)
    fill_in :user_email, with: user.email
    

内容的提问来源于stack exchange,提问作者Creider9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:20:13