Denys
Denys

Reputation: 38

Dockerized Selenium with rails tests

I'm trying to run rspec tests with Selenium chrome in docker but caught dozens of error. Finally i connected capybara to remote capybara, but now i got these errors:

Got 0 failures and 2 other errors:

 1.1) Failure/Error: visit new_user_session_path

      Selenium::WebDriver::Error::WebDriverError:
        unexpected response, code=404, content-type="text/html"
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="utf-8" />
          <title>Action Controller: Exception caught</title> 

....................

      Failure/Error: raise Error::WebDriverError, msg

      Selenium::WebDriver::Error::WebDriverError:
        unexpected response, code=404, content-type="text/html"
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="utf-8" />
          <title>Action Controller: Exception caught</title>
          <style>
            body {
              background-color: #FAFAFA;

...............

So here is my rails_helper.rb. It's really messy cause I tried dozen times with different configs

require 'simplecov'
SimpleCov.start
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 'spec_helper'
require 'rspec/rails'
require 'turnip/capybara'
require "selenium/webdriver"
require 'capybara-screenshot/rspec'
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
# Checks for pending migration and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
Capybara::Screenshot.register_driver(:headless_chrome) do |driver, path|
  driver.browser.manage.window.resize_to(1600, 1200)
  driver.browser.save_screenshot("tmp/capybara/chrom_#{Time.now}.png")
end
url = 'http://test.prs.com:3001/'



Capybara.javascript_driver = :remote_browser

Capybara.register_driver :headless_chrome do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOqptions: { args: %w(headless disable-gpu no-sandbox) }
  )
end
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOqptions: { args: %w(headless disable-gpu no-sandbox) }
  )
Capybara.default_driver = :remote_browser
Capybara.register_driver :remote_browser do |app|
Capybara::Selenium::Driver.new(app, :browser => :remote, url: url,
  desired_capabilities: capabilities)
end
#   Capybara::Selenium::Driver.new app,
#     browser: :chrome,
#     desired_capabilities: capabilities
# end
Capybara.app_host = "http://#{ENV['APP_HOST']}:#{3001}"
Capybara.run_server = false

Capybara.configure do |config|
  config.always_include_port = true
end
Chromedriver.set_version '2.32'
# Capybara.javascript_driver = :headless_chrome
# Capybara.server_host= '0.0.0.0'
# Capybara.default_host = "http://test.prs.com"
# Capybara.app_host = "#{Capybara.default_host}:#{Capybara.server_port}"

RSpec.configure do |config|
  config.include FactoryGirl::Syntax::Methods
  config.include RequestSpecHelper
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.before(:each) do
    DatabaseCleaner.strategy = :truncation
    DatabaseCleaner.clean
 end

  config.before(:all, type: :request) do
    host! 'test.prs.com'
  end

  config.use_transactional_fixtures = true

  config.infer_spec_type_from_file_location!

  config.filter_rails_from_backtrace!

end

And here is my docker-compose.yml:

   version: '3'
    services:
      db:
        image: postgres
      web:
        build: .
        command: bundle exec rails s -p 3001 -b '0.0.0.0'
        volumes:
          - .:/prs
        ports: ['3000:3000', '3001:3001']
          # - "3001:3001"
        depends_on:
          - db
          - selenium
        extra_hosts:
          - "test.prs.com:127.0.0.1"
        environment:
          - APP_HOST=test.prs.com
        links: 
         - selenium
      selenium:
        image: selenium/standalone-chrome-debug:3.6.0-bromine
          # Debug version enables VNC ability
        ports: ['4444:4444', '5900:5900']
          # Bind selenium port & VNC port
        volumes:
          - /dev/shm:/dev/shm
        shm_size: 1024m
        privileged: true
        container_name: selenium

I'm new to all this so any help will be appreciated.

Upvotes: 0

Views: 2034

Answers (2)

ianrandmckenzie
ianrandmckenzie

Reputation: 482

My problem was completely unrelated to Docker, Selenium, Capybara, Chromedriver or any of that. They were all red herrings because on my container only tests related to feature specs were failing.

It turns out they were all failing because feature specs are the only part of the app that looks at IP addresses.

I am using the ip_anonymizer gem and failed to set the IP_HASH_SECRET for the container. Hopefully, anyone else using this gem with Capybara and CI finds this useful.

Upvotes: 0

Thomas Walpole
Thomas Walpole

Reputation: 49870

From the comments you have clarified that you are trying to run the tests in the web docker instance while using the selenium driven browser in the selenium docker instance. Additionally, since your tests are working locally I assume you are using Rails 5.1+ so transactional testing for feature tests will work. Based on those parameters there are a few things needed to make everything work properly.

  1. Capybara needs to start its own copy of the app to run the tests against. This is needed for transactional testing to work and for request completion detection. You enable that with

    Capybara.run_server = true # You currently have this set to false for some reason
    
  2. Capybara needs to run its copy of the app on an interface which can be reached from the selenium docker instance. Easiest way to do that is to specify to bind to 0.0.0.0

    Capybara.server_host = `0.0.0.0`
    Capybara.server_port = 3001 # I'm assuming this is what you want, change to another port and make sure it's reachable from `selenium` instance if desired
    
  3. The driver capybara is using needs to be configured to use the selenium instance

    Capybara.register_driver :remote_browser do |app|
      capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
        chromeOptions: { args: %w(headless disable-gpu no-sandbox) }
      )
    
      Capybara::Selenium::Driver.new(app, 
        :browser => :remote, 
        url: "http://selenium:4444",
        desired_capabilities: capabilities
      )
    end
    
    Capybara.javascript_driver = :remote_browser
    # Capybara.default_driver = :remote_browser # only needed if you want all tests to use selenium rather than just JS tagged tests.
    
  4. Configure Capybara to use the correct host when visiting relative URLs

    Capybara.app_host = "http://web:3001" # a URL that represents the `web` docker instance from the perspective of the `selenium` docker instance
    

Notes: If you were expecting your tests to run on port 3001 as I guessed, then you want to stop the docker instance from launching rails s on that port, since you want the tests run against an app instance that Capybara itself launches # command: bundle exec rails s -p 3001 -b '0.0.0.0'. If the instance you currently have running on 3001 is for something else then you'll need a different port for the tests.

Additionally, if you're not running Rails 5.1+ then you'll need to set config.use_transactional_fixtures = false and fully configure database_cleaner - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example . If you are using Rails 5.1+ then you can probably remove all the database_cleaner stuff.

Upvotes: 2

Related Questions