Question

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
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top