升级Ruby与Heroku至20版本后CircleCI测试失败求助
问题背景
维护一个后端基于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

