Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to RSpec a shared ActiveRecord module without associated database table?

Using RSpec 2.6 / Rails 3.1 / Postgres:

I am writing a supporting module (in my lib/) that any AR model can include. I'd like to write spec for this module. It needs to be included by an AR::Base model, because it loads associations when included and relies on some AR methods, but i don't want to use my existing model when writing rspec for this module.

I would just like to create an arbitrary AR model, but obviously it would not have a table associated in the database and AR is dying. Here's kindda what I want to do:

class SomeRandomModel < ActiveRecord::Base
  include MyModule

  # simulate DB attributes that MyModule would be using
  attr_accessor :foo, :bar, :baz 
end

describe SomeRandomModel do
  it '#some_method_in_my_module' do
    srm = SomeRandomModel.new(:foo => 1)
    srm.some_method_in_my_module.should eq(something)
  end
end

Of course, i get some error in postgres about the relation not existing.

Thanks for your help!

like image 920
janechii Avatar asked Oct 05 '11 23:10

janechii


2 Answers

There is an alternative way to solve this problem using rpsecs shared_examples_for, I mention few tricks in the code snippet but for more info see this relishapp-rspec-guide.

With this you can test your module in any of the classes which include it. So you really are testing what you use in your application.

Let's see an example:

# Lets assume a Movable module
module Movable
  def self.movable_class?
    true
  end

  def has_feets?
    true
  end
end

# Include Movable into Person and Animal
class Person < ActiveRecord::Base
  include Movable
end

class Animal < ActiveRecord::Base
  include Movable
end

Now lets create spec for our module: movable_spec.rb

shared_examples_for Movable do
  context 'with an instance' do
    before(:each) do
      # described_class points on the class, if you need an instance of it: 
      @obj = described_class.new

      # or you can use a parameter see below Animal test
      @obj = obj if obj.present?
    end

    it 'should have feets' do
      @obj.has_feets?.should be_true
    end
  end

  context 'class methods' do
    it 'should be a movable class' do
      described_class.movable_class?.should be_true
    end
  end
end

# Now list every model in your app to test them properly

describe Person do
  it_behaves_like Movable
end

describe Animal do
  it_behaves_like Movable do
    let(:obj) { Animal.new({ :name => 'capybara' }) }
  end
end
like image 100
p1100i Avatar answered Oct 23 '22 14:10

p1100i


I am facing a similar problem, and after much googling I have settled on just setting up and tearing down tables in my RSpec tests. Here's the snippet I've been using:

describe "stuff you are testing do" do

  before :all do
    m = ActiveRecord::Migration.new
    m.verbose = false  
    m.create_table :random_class do |t| 
      t.string :field_1
      t.integer :field_2
    end
  end

  after :all do
    m = ActiveRecord::Migration.new
    m.verbose = false
    m.drop_table :random_class
  end

  class RandomClass < ActiveRecord::Base
    attr_accessible :field_1, :field_2
  end

  # Your regular RSpec before(:each) blocks and tests
  # ...

  # e.g.
  it "should be able to use RandomClass" do
    rc = RandomClass.create! :field_1 => "hello", :field_2 => 5
    rc.field_1.should == "hello"
    rc.field_2.should == 5
  end

end

I'm not enamored of this solution, but it works. Hope that's helpful for someone! Either that or inspires them to post the best way to accomplish this.

:)

like image 40
goggin13 Avatar answered Oct 23 '22 15:10

goggin13