Search code examples
ruby-on-railsrubyseleniumselenium-webdrivercapybara

ArgumentError when running Capybara tests on Ruby 3.0


I am really stuck. I am upgrading my Rails app to Ruby 3 (from 2.7). When running tests, I always run into this issue when I visit a path:

state = "new"
visit status_path(state: “state")

I receive the following error when running rspec:

Capybara starting Puma...
* Version 5.6.4 , codename: Birdie's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:58568
ArgumentError: wrong number of arguments (given 2, expected 1)
from ~/.rbenv/versions/3.0.5/lib/ruby/3.0.0/net/protocol.rb:116:in `initialize'

My Gemfile is as such:

gem "capybara"              # 3.38.0
gem "selenium-webdriver"    # 4.8.0 
gem "webdrivers"            # 5.2.0

(They're all on the latest version)

My setup doesn't look wrong:

require "webdrivers/chromedriver"

Webdrivers.cache_time = 86_400 # 1 day

Capybara.register_driver :headless_chrome do |app|
  Capybara::Selenium::Driver.load_selenium

  browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |options|
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-gpu")
  end

  Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end


Capybara.javascript_driver = :headless_chrome

Troubleshooting:

  • I tried Puma 6 - same issue.
  • The controllers at status_path is not even hit. This errors occurs right after Puma loads up.
  • I do not think it's the Capybara setup, and I just cannot find where it is calling the Ruby 3 library wrong (net/protocol).
  • I downgraded capybara to 3.37.1, and same issue.

Thank you


Solution

  • FYI Upgrading from 2.7 to 3 you will more often than not see this error. It's highly likely that some code you were previously using in 2.7 will be not correctly hashing some args.

    The first hit when googling this will take you back to SO (Won't share link as SO doesn't like links). But to paraphrase from the official ruby byline when updating

    Separation of positional and keyword arguments in Ruby 3.0:

    In most cases, you can avoid incompatibility by adding the double splat operator. It explicitly specifies passing keyword arguments instead of a Hash object. Likewise, you may add braces {} to explicitly pass a Hash object, instead of keyword arguments.

    TL;DR - Try doing splatting your hash args collection kwargs -> **kwargs - Your rails path likely isn't a kwarg but a hash --> { key: value }

    EDIT: Reasoning (If you're interested), is that prior to ruby3. Ruby would try assess and guesstimate what you meant. From ruby3 onwards it has made a change and fill forcibly use what you give it (A lot of people used to use kwargs but wanted them treated as a single hash, now you need to stipulate this!)