Skip to content

Instantly share code, notes, and snippets.

@mcrowe
Last active May 28, 2020 20:38
Show Gist options
  • Save mcrowe/11071655 to your computer and use it in GitHub Desktop.
Save mcrowe/11071655 to your computer and use it in GitHub Desktop.
Saving and restoring Capybara sessions
module CapybaraSessionFactory
class SessionAlreadyDefined < StandardError; end
class SessionNotDefined < StandardError; end
# Parse a capybara session mode, which has the form
# "[driver]:[session_name]:[application_id]"
#
class CapybaraModeParser
attr_reader :driver, :session_name, :application_id
def initialize(mode)
@mode = mode
parse
end
private
def parse
@driver, @session_name, @application_id = @mode.split(':')
end
end
class Session
include Capybara::DSL
def initialize(name, setup_proc)
@name, @setup_proc = name, setup_proc
@is_setup = false
end
def setup?
@is_setup
end
def run_setup
return if setup?
instance_eval(&@setup_proc)
@is_setup = true
end
def capybara_session_name
"capybara_session_factory_#{@name}"
end
end
class << self
# Defines a new session and its setup code.
#
def define_session(name, &setup_proc)
raise SessionAlreadyDefined if sessions.key?(name)
sessions[name] = Session.new(name, setup_proc)
end
# Loads a session by name.
# The setup code is only run on the first load.
#
def load_session(name)
raise SessionNotDefined unless sessions.key?(name)
session = sessions[name]
Capybara.using_session(name) do
session.run_setup unless session.setup?
yield
end
end
private
def sessions
@sessions ||= {}
end
# Returns true if the capybara mode string corresponds to a
# CapybaraSessionFactory session.
#
def managed_session_mode?(mode)
parser = CapybaraModeParser.new(mode)
sessions.key?(parser.session_name)
end
end
end
class << Capybara
# Force capybara not to reset sessions that are managed by CapybaraSessionFactory.
# This let's sessions persist between tests.
#
# TODO: Capybara doesn't allow management of its "session_pool", so we need to monkey-path.
# Capybara should be updated with a session manager strategy so this is not required.
#
def reset_sessions!
session_pool.each do |mode, session|
session.reset! unless CapybaraSessionFactory.managed_session_mode?(mode)
end
end
alias_method :reset!, :reset_sessions!
end
# spec/features/dashboard_spec.rb
require 'spec_helper'
describe 'the dashboard' do
it 'has a welcome message' do
CapybaraSessionFactory.load_session('signed in') do
click_link 'Dashboard'
expect(page).to have_content 'Welcome to your dashboard'
end
end
end
# spec/sessions/signed_in.rb
CapybaraSessionFactory.define_session 'signed in' do
visit '/signup'
fill_in 'First name', with: 'Bob'
fill_in 'Email', with: 'bob@gmail.com'
fill_in 'Password', with: 'password'
click_button 'Sign Up'
end
@mcrowe
Copy link
Author

mcrowe commented Apr 19, 2014

This is a proof-of-concept that allows you to store and restore sessions in Capybara. Under certain conditions, this could greatly increase the speed of an integration suite without coupling tests.

By default, Capybara resets all session variables after every test. This is good, because it decouples your tests. However, integration suites often end up with a ridiculous amount of duplicated setup steps. For example, its often common to have hundreds of tests that fill out the same login or registration forms with the same credentials just to get to their starting point. Sure, ideally you'd have stubbed out that setup, but in the real world that doesn't always happen. This gist provides a way to share sessions between tests in a safe way, so that you can run setup steps once per suite, and spend cpu cycles where they counts.

This was thrown together pretty quickly. It proved effective on a sample application, but there are probably some tweaks required before its ready for major use. Notably, I will probably need to add the ability to define setup code that must happen every time a session is loaded and not just the first. This would allow you to create any records that must exist for the session.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment