Solving [DEPRECATION] last_comment is deprecated when upgrading to rails 4.2.6

We ran into the deprecation warning above when we upgraded to Rails 4.2.6.

The issue was an older version of rspec-rails 3.3.3 and upgrading to 3.4.2 removed the warning.

To resolve all dependencies we edited our Gemfile to:

gem 'rspec-rails' , '3.4.2'
gem "rspec-activemodel-mocks" #, "~> 1.0.1"
gem "rspec-mocks", "~> 3.4.0"

then ran:
bundle update rspec-rails rspec-mocks accept_values_for

Getting RSpec, Postgres, DatabaseCleaner to play nicely together

Did a quick benchmark last week to find the optimal way to configure Rspec on our projects that use Postgres and DatabaseCleaner

One of our test suites takes 3.5 minutes to run when we do NOT use DatabaseCleaner, the default rspec setup:

# spec_helper.rb
#config.use_transactional_examples = false 
#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

When we enabled database cleaner (with default .strategy = :transaction), the test still took 3.5 minutes, but we got lots of warnings:**WARNING: there is already a transaction in progress
NOTICE: there is no transaction in progress
**

# spec_helper.rb
 #config.use_transactional_examples = false 
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

However, when we changed the .strategy to :trunctation, it eliminated the warnings, but slowed the tests down to 8 minutes!# spec_helper.rb

#config.use_transactional_examples = false 
config.before(:suite) do 
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation) 
end 
config.before(:each) do 
DatabaseCleaner.start 
end 
config.after(:each) do 
DatabaseCleaner.clean 
end

The best results were to leave the strategy set to :transaction, but set config.use_transactional_examples = false. That yielded a runtime of about 3.5 minutes.

# spec_helper.rb
 config.use_transactional_examples = false 
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

We used the following addition spec to verify that the database was being cleaned. (run this with –order default )

#dbclean_spec.rb

require "spec_helper" 
Refer.destroy_all 
describe Refer do 
it "has none to begin with" do 
  Refer.count.should == 0 
end 
it "has one after adding one" do 
Refer.create 
Refer.count.should == 1 
end 
it "has none after one was created in a previous example" do Refer.count.should == 0 
end 
end

Rspec gotcha – instance variables are not cleared between ‘get’s

It seems that (unlike when controller methods are invoked via browser) if you do two consecutive get’s with rspec, instance variables are not cleared with each get, so data crossover is possible.

Here’s a failing test, showing that a variable set in ‘vartest1′ is still present when another controller method ‘vartest2′ is run:

Controller methods:

def vartest1 
  @this_var = "foo" 
  render :text => @this_var
end 
def vartest2 
  render :text => @this_var # should be EMPTY! 
end

Rspec controller spec (note: we use render_views so the views are rendered in the spec)

describe "instance variable crossover example", :focus => true  do 
  describe "THIS PASSES put each get in separate contexts" do 
    it "vartest1 outputs foo" do 
      get "vartest1" 
      response.body.should include("foo") 
    end 
    it "vartest2 does NOT output foo" do 
      get "vartest2" 
      response.body.should_not include("foo") 
    end 
  end 
  describe "THIS FAILS put both gets in SAME context" do 
    it "should not crossover controller instance varables" do 
      get "vartest1" 
      response.body.should include("foo") 
      get "vartest2" 
      response.body.should_not include("foo") # THIS FAILS 
    end 
  end 
end

Rspec results:

instance variable crossover example 
THIS PASSES 

put each get in separate contexts 
vartest1 outputs foo 
vartest2 does NOT output foo 
THIS FAILS 

put both gets in SAME context should not crossover 
controller instance varables (FAILED - 1)

>

What’s happening in the failing test is that when rspec does get ‘vartest1′ the controller method set an instance variable to ‘foo’, and when rspec does get ‘vartest2′ the instance variable (which should be nil) is still set, so the test fails.

How to specify traits for model associations in FactoryGirl

If you’re using RSpec and FactoryGirl to automate testing for your Rails app, you probably know that when creating a factory for a model that has association you can simply specify the factory of the associated model. For example, suppose your Contact model has associations with Phone and Store models:

FactoryGirl.define do factory :contact do |f| f.name { Faker::Name.name } phone store end end

And you probably know you can have traits for factories, for example, different settings for different types of stores:

FactoryGirl.define do 
  factory :store do |f| 
    after(:create) { |store| 
      store.update_attributes :name => Faker::Name.last_name + " LLC" 
    } 

    trait :unknown do 
      after(:build) { |store| store.init_blank(Settings.feature_set_unknown) } 
    end 

    trait :restaurant do 
      after(:build) { |store| store.init_blank(Settings.feature_set_restaurant) } 
    end 

    trait :retailer do 
      after(:build) { |store| store.init_blank(Settings.feature_set_retailer) } 
    end 
  end
end

However, what we could not find documented anywhere was how to specify a trait for an associations. Say when you create a contact factory you want specify which type of store that contact belongs to:

Here’s the magic decoder ring… use
:factory => [:association_factory_name, :trait_name]

FactoryGirl.define do 
  factory :contact do |f| 
    f.name { Faker::Name.name } 
    phone association :store, :factory => [:store, :unknown] 

    trait :foodie_contact do 
      association :store, :factory => [:store, :restaurant] 
    end 

    trait :retailer_contact do 
      association :store, :factory => [:store, :retailer] 
    end 
  end 
end

Which you can then invoke as follows in your examples:

c = FactoryGirl.create(:contact, :retailer_contact)