I'm using this solution to avoid issues with the database during javascript tests.
The first run through the suite, the tests run fine, all passing.
If I run the entire suite again, they'll still pass.
But, if I run an individual spec file and then try to run the suite (or other individual test), I get this error:
An error occurred in an after hook
ActiveRecord::StatementInvalid: ArgumentError: prepare called on a closed database: rollback transaction
occurred at /home/steveq/.rvm/gems/ruby-1.9.3-p194@rails32/gems/sqlite3-1.3.7/lib/sqlite3/database.rb:91:in `initialize'
1) Signing up with valid information
Failure/Error: visit "/sign_up"
ActiveRecord::StatementInvalid:
ArgumentError: prepare called on a closed database: PRAGMA table_info("users")
# ./app/controllers/registrations_controller.rb:3:in `new'
# ./app/controllers/registrations_controller.rb:3:in `new'
# ./spec/features/sign_up_feature_spec.rb:5:in `block (2 levels) in <top (required)>'
If I reload guard, the tests will pass again.
Does anyone have any insight into what's happening here or any possible solutions? I've tried every variation I can think of, and here's my spec_helper file to show the things I've tried (the variations are commented out, the current code - what's suggested on the capybara page - is what I'm using now).
require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
#require 'spork/ext/ruby-debug'
Spork.prefork do
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
#Make it so Selenium (out of thread) tests can work with transactional fixtures
#REF http://opinionated-programmer.com/2011/02/capybara-and-selenium-with-rspec-and-rails-3/#comment-220
# ActiveRecord::ConnectionAdapters::ConnectionPool.class_eval do
# def current_connection_id
# # Thread.current.object_id
# Thread.main.object_id
# end
# end
# FactoryGirl short syntax
config.include FactoryGirl::Syntax::Methods
config.use_transactional_fixtures = true
# set up for use with :js => true.
# See http://stackoverflow.com/questions/8178120/capybara-with-js-true-causes-test-to-fail for more info
# config.before :suite do
# if Capybara.current_driver == :rack_test
# DatabaseCleaner.strategy = :transaction
# else
# DatabaseCleaner.strategy = :truncation
# end
# DatabaseCleaner.start
# end
# config.after do
# DatabaseCleaner.clean
# end
# standard RSPEC config
# config.before(:suite) :truncation
# else
# :transaction
# end do
# DatabaseCleaner.strategy = if example.metadata[:js]
# :truncation
# else
# :transaction
# end
# DatabaseCleaner.clean_with(:truncation)
# end
# config.before(:each) do
# DatabaseCleaner.start
# end
# config.after(:each) do
# DatabaseCleaner.clean
# end
# config.before(:each) do
# DatabaseCleaner.strategy = if example.metadata[:js]
# :truncation
# else
# :transaction
# end
# DatabaseCleaner.start
# end
# config.after(:each) do
# DatabaseCleaner.clean
# end
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
config.include MailerMacros
config.include LoginMacros
config.before(:each) { reset_email }
config.include Devise::TestHelpers, :type => :controller
config.extend LoginMacros, :type => :controller
end
end
Spork.each_run do
# allows capybara JS tests to run in separate thread
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
# This code will be run each time you run your specs.
load "#{Rails.root}/config/routes.rb"
FactoryGirl.reload
# reload all the models
Dir["#{Rails.root}/app/models/**/*.rb"].each do |model|
load model
end
end
So here's what I figured out - hopefully it will help anyone else who gets into this same trouble.
Firstly, the method I was using:
Spork.each_run do
# allows capybara JS tests to run in separate thread
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
# This code will be run each time you run your specs.
load "#{Rails.root}/config/routes.rb"
FactoryGirl.reload
# reload all the models
Dir["#{Rails.root}/app/models/**/*.rb"].each do |model|
load model
end
end
works just fine, but not, it seems, with sqlite.
The quickest fix was simply to swap out sqlite as my test db for mysql. That solved everything.
Another solution, one that I'm really digging right now, was to drop spork completely in favor of Zeus.
You can check it out via the github link, but I'll tell you why I like it.
It has no necessary config - no spork block in the spec_helper, no spork block in the guardfile.
It also speeds up server and console initialization to under a second- not a huge deal, but very, very pleasant.
My test suite (191 examples so far) went from running in around 35 seconds to 17.5 seconds - half the time.
I urge you to check it out.