Spork and cache_classes problem with rspec, factory_girl and datamapper
Asked Answered
F

3

13

I've got a problem with Spork test server.

If I set config.cache_classes = false in config/environments/test.rb then specs start to rasie errors.

Failure/Error: task = Factory(:something, :foo => @foo, :bar => @bar)
     DataMapper::ImmutableError:
       Immutable resource cannot be modified

This is my spec_helper.rb:

require 'spork'

Spork.prefork do
  if ENV['CODE_COVERAGE'] == '1'
    require 'simplecov'
    SimpleCov.start 'rails'
  end

  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'webmock/rspec'
  require 'factory_girl'

  Dir[Rails.root.join("spec/controllers/shared/*.rb")].each { |f| require f }
  Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

  RSpec.configure do |config|
    config.mock_with :mocha
    config.include Rails.application.routes.url_helpers
    config.include UrlHelper

    config.before(:each) do
      repository(:default) do
        transaction = DataMapper::Transaction.new(repository)
        transaction.begin
        repository.adapter.push_transaction(transaction)
      end
    end

    config.after(:each) do
      repository(:default).adapter.pop_transaction.try(:rollback)
    end

  end
end

# This code will be run each time you run your specs.
Spork.each_run do
  # reload factories
  Factory.definition_file_paths = Dir[File.join(Rails.root, "spec", "factories")]
  Factory.find_definitions

  DatabaseCleaner.strategy = :truncation
  DatabaseCleaner.clean
  LoggedEvent.all.destroy!

end

When I have config.cache_classes = true, then everything works well, but It not reload me a models, controllers classes, so I don't see a point in using spork in this case.

I tried to add to spec_helper.rb something like this, when cache is true:

Spork.each_run do
    Dir.glob("#{Rails.root}/app/models/*.rb").sort.each { |file| load file }
end

But I don't like this solution.

Functional answered 6/5, 2011 at 15:1 Comment(0)
S
20

Just add:

ActiveSupport::Dependencies.clear

to the end of the prefork block. this will take care of clearing the models.

Also, you want to move that rspec configure inclusions to Spork.each_run, same goes with requiring spec support and shared files.

This works, I use this setup in 2 projects w/o any troubles.

Scullion answered 20/5, 2011 at 16:35 Comment(8)
Wait, so does ActiveSupport::Dependencies.clear belong in prefork or each_run? I've seen conflicting stories. Neither seems to work for me, both causing loading issues: "expected xxxx to define xxxx." Also, is it ActiveSupport::Dependencies.clear alone that solves the problem? Or is that needed in conjunction with the each_run example in the original question?Aronoff
I don't know why, but my project just suddenly started doing this, for no apparent reason. I ended up putting the Dependencies.clear in my Spork.each_run, and it worked for me. I don't have my Rspec.config in each_run, just: FactoryGirl.reload and ActiveSupport::Dependencies.clear. Hope that helps.Auriscope
ActiveSupport::Dependencies.clear should be added at the end of the prefork, not each_runCreighton
Unfortunately this solution didn't work for me either - I've tried it on a couple of projects nowMoron
You guys want to make sure that your test db has been migrated. Hope all is well, @PeterNixey =) Great seeing you here :-)Taveda
@Taveda - likewise buddy. Hope all's wellMoron
This throws errors for me - some weird thing where it's trying to instantiate a module. Haven't tracked it down though, so maybe it's very specific to my app.Viva
This approach is sort of a workaround. My guess is that the real problem is that you have code running in Spork.prefork that's loading models. I had a similar issue and I was able to debug it by calling spork --diagnose. It will list all of the files loaded by Spork and a backtrace of what loaded each file. In my case I had an initializer referencing a controller, causing issues with that controller not reloading on each test run.Survance
V
8

Try ActionDispatch::Reloader

In addition to setting config.cache_classes = false in config/environments/test.rb, the following seems to make DataMapper and Spork play nicely for our team:

Spork.each_run do

  # Routes
  MyApp::Application.reload_routes!

  if Spork.using_spork?
    # Reload all app files
    ActionDispatch::Reloader.cleanup!
    ActionDispatch::Reloader.prepare!

    # All factories
    FactoryGirl.reload
  end

end
Viva answered 23/5, 2012 at 19:9 Comment(2)
Nice. I didn't expect it to, but this seemed to fix my problems with Mongoid and Spork. All the other documentation on the subject (Spork.trap_method Jujitsu and ActiveSupport::Dependencies.clear) didn't work at all. I was even able to get rid of my extra trap on Rails::Mongoid.load_models.Ceramal
This approach actually includes ActiveSupport::Dependencies.clear and more. Calling ActionDispatch::Reloader.cleanup! and prepare! is exactly what reload! does in the rails console. +1Maximo
D
0

Hi I wanted to share what helped me running test again.
It turns out I had to add the shared_connection trick as spork was complaining about connection lost.
After this I had to shift the dependency.clear line at at the end of the block as it was caching my models.

Spork.each_run do
  # ActiveSupport::Dependencies.clear # <-- moved this line at end of block as suggested by @solnic
  ActiveRecord::Base.instantiate_observers
  MyApp::Application.reload_routes!
  FactoryGirl.reload

  class ActiveRecord::Base
    mattr_accessor :shared_connection
    @@shared_connection = nil

    def self.connection
      @@shared_connection || retrieve_connection
    end
  end
  ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

  ActiveSupport::Dependencies.clear # <-- line moved here

end if Spork.using_spork?
Dragonet answered 15/3, 2013 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.