capybara/selenium with rspec before :all hook

2020-02-28 03:09发布

问题:

In an attempt to reduce the number of page visits with selenium, I wanted to call the visit method from a before :all hook and run all my examples with a single page load. However, when I specify before :all vs before :each, the browser opens, but the url is never visited. Below is a simplified and contrived example...

describe 'foobar', :js => true do
  before :all do
    Capybara.default_wait_time = 10
    obj = Factory(:obj)
    visit obj_path(obj)
  end

  it 'should have foo' do
    page.should have_content('foo')
  end

  it 'should have bar' do
    page.should have_content('bar')
  end
end

When I set it to before :each, it works, but the page loads twice. Is this a limitation of Capybara?

回答1:

Cause of problem

The second example doesn't work because because Capybara resets the session after each RSpec example; the page you visit-ed in your before :all block is no longer open at that point. This an explicit behavior of Capybara. It's in the capybara gem, under /lib/capybara/rspec.rb:

config.after do
  if self.class.include?(Capybara::DSL)
    Capybara.reset_sessions!
    Capybara.use_default_driver
  end
end

I Googled around for a couple of hours and found several others struggling with this, to no avail really.

I also found that a patch that would allow Capybara to be configured not to reset the session after each example has been proposed ... but Capybara creator jnicklas declined the pull request.

Solution

The quickest -- though perhaps not the best -- workable solution I've found (so far) is to monkey-patch Capybara thusly:

module Capybara                                                                                                                                                                                  
  class << self                                                                                                                                                                                  
    alias_method :old_reset_sessions!, :reset_sessions!                                                                                                                                          
    def reset_sessions!; end                                                                                                                                                                     
  end                                                                                                                                                                                            
end

This just makes reset_sessions! do nothing when it gets called. Note: Beware of unintended side-effects! (You can always revert the alias_method later on in your code if you need the default resetting behavior to happen again.)