public
Last active

Active Record Spec Helper - Loading just active record

  • Download Gist
.rspec
1 2
--colour
-I app
active_record_spec_helper.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
require 'active_record'
require 'database_cleaner'
 
connection_info = YAML.load_file("config/database.yml")["test"]
ActiveRecord::Base.establish_connection(connection_info)
 
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
coderetreat.rb
Ruby
1 2 3 4 5
class Coderetreat < ActiveRecord::Base
def self.running_today
where(scheduled_on: Date.today)
end
end
coderetreat_spec.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
require 'active_record_spec_helper'
require 'models/coderetreat'
 
describe Coderetreat do
describe ".running_today" do
it "returns a coderetreat scheduled for today" do
coderetreat = Coderetreat.create! city: "Chicago", scheduled_on: Date.today
Coderetreat.running_today.all.should =~ [coderetreat]
end
 
it "does not return a coderetreat not scheduled for today" do
coderetreat = Coderetreat.create! city: "Chicago", scheduled_on: Date.today.advance(:days => -1)
Coderetreat.running_today.should be_empty
end
end
end
spec_helper.rb
Ruby
1 2 3 4 5 6 7 8 9
#Some databases get upset if you try to start a new transaction while a transaction is already in play, so running the whole spec suite chokes when rspec is trying to start a transaction. You need to update your spec_helper to rely on active_record_spec_helper to do this for you.
 
#Replace this line
config.use_transactional_fixtures = true
 
#With this
require 'active_record_spec_helper'
 
#TADA

Thanks. It is a very handy addition.

Thanks! I wasn't sure if you were doing something to mock AR, or just isolating AR stuff to one spec helper.

No. I don't agree with mocking AR. This is really about a step forward in making progress towards a realistic approach to keeping things fast.

I have started wrapping my active_record_spec_helper in an "unless defined?(Rails)" block so that I can run the model specs alone or as part of the entire suite.

@rubysolo I've not found the need to do that in order to have them run in both situations. What error are you seeing that causes you to need this?

Interesting. If I run "rake", I get the following error:

activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:645:in `async_exec': PG::Error: connection is closed: SELECT tablename (ActiveRecord::StatementInvalid)
FROM pg_tables
WHERE schemaname = ANY (current_schemas(false))

which traces back to:

DatabaseCleaner.clean_with(:truncation)

in the active_record_spec_helper.

Does your spec_helper set the config.use_transactional_fixtures? Take that out, replace by requiring this spec helper in your regular spec helper

@coreyhaines Indeed it did, and this fixed the problem. :) Thanks!

@rubysolo Thanks so much for bringing this up. Glad it worked. I forgot to put that in here, so now we have it documented in the comments. :) I'll try to get it into the gist, itself, as well.

@coreyhaines is there a need for the double requires of "database_cleaner"? Isn't it unnecessary in the spec_helper?

@rmcstil A good point.

@coreyhaines Thanks for posting this! Although I'm starting to get spoiled by the near instantaneous feedback and am considering dropping model specs all together. Right now I've got it costing me about 6 seconds to load active record. Is that what you do with mercuryapp?

@rmcastil Yeah, that's the eventual goal. I tend to only want to load active record when I'm dealing with scopes. Those make sense to have the database loaded up.
This technique is very useful to shrink the feedback loop, so you can start effectively making changes to your design.

@coreyhaines Instead of DatabaseCleaner you could do something like this: ActiveRecord::Base.subclasses.each(&:delete_all) DatabaseCleaner is such a beast to load.

Thanks @revans. I'll try that.

Very glad that you gist'd this. I had similar thoughts as @steveklabnik. I appreciate the insight!

@coreyhaines

Not using database_cleaner and using the below code cut off 0.2-0.3 sec.

  config.around do |example|
    ActiveRecord::Base.transaction do
      example.run
      raise ActiveRecord::Rollback
    end
  end

@brandonhilkert Thanks. I'm going to try that.

What happens with a class with a lot of relations? We may end up manually requiring a lot of things, won't we? Or is there a catch that I'm not seeing?

@brandonhilkert that's cool! But if you use capybara-webkit for example, this doesn't work :(

@brandonhilkert I mean, unless you workaround https://gist.github.com/4469873

Two things I've noticed:

  • I had to require the model like this require_relative '../../app/models/coderetreat
  • When loading the full stack I had an ActiveModel::MassAssignmentSecurity::Error which passed with this approach

Not sure if I'm doing anything wrong. The idea is awesome though.

@zamith, I had the same problem. Were you sure to add the -I app line to your .rspec file? Then you require 'models/coderetreat'. That fixed it for me.

If you are using Rails 4 with SQLite and database_cleaner 1.1.1 you get this error
undefined local variable or method `postgresql_version'

to solve the problem downgrade to database_cleaner 1.0.1

Thank you @coreyhaines for sharing this.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.