2

I have a module that extends ActiveSupport::Concern. Here is the included block:

included do
  after_save :save_tags

  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
end

How can I stub out these calls? I have tried a few ways, but Ruby complains that these methods don't exist when I try and test the module in isolation.

Thanks!

Fire Emblem
  • 5,961
  • 3
  • 24
  • 37
  • I think you can look at my answer to the similar problem http://stackoverflow.com/questions/15868210/testing-a-concern-module-that-uses-activerecord/26607213#26607213 – freemanoid Oct 28 '14 at 11:51

2 Answers2

3

I think you have a couple options, depending on what it is that you want to test.

If you just want to test that the module actually sets up the has_many associations and after_save call back, then you could set up a simple rspec expectation:

class Dummy
end

describe Taggable do
  it "should setup active record associations when included into models" do
    Dummy.should_receive(:has_many).twice
    Dummy.should_receive(:after_save).with(:save_tags)
    Dummy.send(:include, Taggable) # send is necessary because it's a private method
  end
end

You probably can test the save_tags method pretty easily without further mocking, but if you want to test behavior that depends on the has_many associations being setup you might create another Dummy class with has_many and after_save stubbed out, but with accessors for the associations:

class Dummy
  attr_accessor :taggings, :tags

  # stub out these two
  def self.has_many
  end

  def self.after_save
  end

  def initialize
    @taggings = []
    @tags = []
  end

  include Taggable
end

describe Taggable do
  it "should provide a formatted list of tags, or something" do
     d = Dummy.new
     d.tags = [double('tag')]
     d.formatted_tags.size.should == 1
  end
end

We can clean that (somewhat brittle test class) up with a bit of meta programming, though it's up to your judgement on whether this makes the test too difficult to understand.

class Dummy
  def self.has_many(plural_object_name, options={})
    instance_var_sym = "@#{plural_object_name}".to_sym

    # Create the attr_reader, setting the instance var if it doesn't exist already
    define_method(plural_object_name) do
      instance_variable_get(instance_var_sym) ||
          instance_variable_set(instance_var_sym, [])
    end

    # Create the attr_writer
    define_method("#{plural_object_name}=") do |val|
      instance_variable_set(instance_var_sym, val)
    end
  end

  include Taskable
end
Dhruv
  • 1,380
  • 12
  • 20
0

Before your test starts, you need to make sure you first load the file with the original definition of ActiveSupport::Concern and after that load you have to load your extention. Be sure to load the correct files as the rails autoloading mechanism will find your extention in favor of the original.

moritz
  • 25,477
  • 3
  • 41
  • 36