Selenium Chrome 没有看到在 Rails Rspec Docker 环境中启用事务固定装置的数据库更改

Selenium Chrome not seeing database changes with transactional fixtures enabled in Rails Rspec Docker environment

我距离让我的 Selenium 测试在我的新 Docker 开发环境中工作还差得很远。

我最近从 ruby 2.4.2 升级到 2.6.3。大约在同一时间,我也从本地环境切换到 Docker 的设置。除了最后一期,一切都顺利迁移。

用浏览器测试,好像浏览器看不到数据库的变化,比如我创建用户,然后在浏览器上通过web登录UI,返回的网页显示 "user and pass doesn't not exist"。此外,即使在测试过程中,更改也不会出现在数据库中,尽管我认为启用 transactional_fixtures 时这是正常的。

问题在 config.use_transactional_fixtures = false 时消失。但后来我不得不处理数据库清理,我尝试过但也有问题。请注意,这一切在我本地 MacOS 上的 ruby 2.4.2 中运行良好。

我可以通过端口 5900 上的 VNC 访问浏览器并查看测试 运行 正常,直到它需要做任何需要来自数据库的数据,比如通过浏览器使用创建的用户名登录以编程方式在规范中。

我不清楚数据库信息如果不在数据库中会去哪里,或者浏览器如何访问这些数据?这个 article 似乎讨论了 Capybara 和 web 服务器共享同一个数据库连接以访问未提交的数据库更改的相关问题。但是过了几天我还是迷路了‍♂️

这是我的配置。

Docker文件

FROM ruby:2.6.3
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client pdftk xvfb

RUN wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip
RUN unzip chromedriver_linux64.zip
RUN mv chromedriver /usr/bin/chromedriver
RUN chown root:root /usr/bin/chromedriver
RUN chmod +x /usr/bin/chromedriver

RUN echo "chromedriver -v"
RUN chromedriver -v
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
ENV BUNDLER_VERSION='2.0.2'
RUN gem install bundler --no-document -v '2.0.2'

RUN echo $BUNDLER_VERSION
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose.yml

version: '3'
services:
  db:
    image: postgres:9.6.15
    volumes:
      - data:/var/lib/postgresql/data
    environment:    
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
    ports:
      - "5432:5432"
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && RAILS_ENV=test bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
    links:
      - selenium
  redis:
    image: redis:alpine
    command: redis-server
    ports:
      - "6379:6379"
  sidekiq:
    depends_on:
      - db
      - redis
    build: .
    command: sidekiq -c 1 -v -q default -q mailers
    volumes:
      - '.:/myapp'
    env_file:
      - '.env'

  selenium:
    image: selenium/standalone-chrome-debug:3.0.1-germanium
      # Debug version enables VNC ability
    ports: ['4444:4444', '5900:5900']
      # Bind selenium port & VNC port
    logging:
      driver: none
      # Disable noisy logs.

volumes:
  data: 

spec/rails_helper.rb

# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
require 'support/factory_bot'
require 'support/session_helpers'
require 'support/record_helpers'

@both = ['artist','gallery']

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  # RSpec Rails can automatically mix in different behaviours to your tests
  # based on their file location, for example enabling you to call `get` and
  # `post` in specs under `spec/controllers`.
  #
  # You can disable this behaviour by removing the line below, and instead
  # explicitly tag your specs with their type, e.g.:
  #
  #     RSpec.describe UsersController, :type => :controller do
  #       # ...
  #     end
  #
  # The different available types are documented in the features, such as in
  # https://relishapp.com/rspec/rspec-rails/docs
  config.infer_spec_type_from_file_location!

  # Filter lines from Rails gems in backtraces.
  config.filter_rails_from_backtrace!
  # arbitrary gems may also be filtered via:
  # config.filter_gems_from_backtrace("gem name")


  config.include Features::SystemTestHelpers#, type: :system

end

# fixes a glitch in most recent chromedriver or chrome where it can't access remote URLs
# https://github.com/teamcapybara/capybara/issues/2181
require "selenium/webdriver"

Capybara.app_host = "http://web:3000"
Capybara.javascript_driver = :selenium_chrome_headless
Capybara.run_server = false

Capybara.register_driver :selenium_chrome_headless do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: { args: %w(no-default-browser start-maximized enable-features=NetworkService,NetworkServiceInProcess) }
  )
  Capybara::Selenium::Driver.new app, browser: :remote, url: "http://selenium:4444/wd/hub", desired_capabilities: capabilities
end

entrypoint.sh

#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

bundle install

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

正在破坏的规范摘录

spec/system/record_gallery_spec.rb

require 'rails_helper'

feature 'User creates a work' do

  scenario 'with most fields filled out', driver: :selenium_chrome_headless do 
    gallery_sign_in

    visit new_work_path
    # breaks here ^^

    ...

  end
end

上面引用的辅助函数

def gallery_sign_in(user: nil, id: nil, custom_pdf: nil)
  user = create(:user, id: id, custom_pdf: custom_pdf) unless user
  user.role = 'gallery'
  user.save

  visit login_path
  fill_in 'user[email]', with: user.email
  fill_in 'user[password]', with: user.password
  click_button 'Login'
  return user
end

Rails 5.1 确实支持在被测应用程序和测试(事务测试)之间共享数据库连接,但前提是您让 Capybara 启动被测应用程序的实例,因为测试和该应用程序需要 运行 在同一进程下作为单独的线程运行。您明确告诉 Capybara 不要 运行 被测应用程序 (Capybara.run_server = false),而是告诉它 运行 针对您单独启动的应用程序实例 (Capybara.app_host = "http://web:3000" ).在该配置中,无法在测试和 AUT 之间共享数据库连接,因此您必须禁用事务测试 ('config.use_transactional_fixtures = false') 并使用 database_cleaner(或其他类似的东西)来处理重置数据库在每次测试之间。