jdliss / shoulda-callback-matchers

Test existence of your Rails callbacks without having to call them
MIT License
134 stars 18 forks source link

Callbacks with Proc condition not working #20

Open justDax opened 8 years ago

justDax commented 8 years ago

hello and thank you for your work, I have the following test case that doesn't seem to work:

test class

class Template::Box < ActiveRecord::Base
  before_validation :set_initial_position, if: ->(box){ box.position.blank? }, on: :create

  private

    def set_initial_position
      self.position = (self.brothers.maximum("position") || -1) + 1
    end
end

test spec is_expected.to callback(:set_initial_position).before(:validation).on(:create).if( subject.position.blank? )

justDax commented 8 years ago

for now It seems to work locally with a quick fix to the file "active_model.rb":

def matches_conditions? subject, callback
  if rails_version >= '4.1'
    # !@condition || callback.instance_variable_get(:"@#{@condition_type}").include?(@condition)
    !@condition || callback.instance_variable_get(:"@#{@condition_type}").map{|cond| cond.is_a?(Proc) ? cond.call(subject) : (cond == @condition) }.include?(true)
  else
    !@condition || callback.options[@condition_type].include?(@condition)
  end
end

and also by changing this line in the "matches?" method: matches_conditions?(callback) && to matches_conditions?(subject, callback) &&

beatrichartz commented 8 years ago

Interesting - the reason this works is that you set the @condition to true, which might be a misunderstanding.

I'd suggest to set this up to either stub the position in a context like so:

context "with no position set" do
  before do
    subject.stub(:position, nil)
  end
  it { is_expected.to callback(:set_initial_position).before(:validation).on(:create) }
end

Or, if you want to use the if chain, devise a private method for position presence like so:

Implementation:

class Template::Box < ActiveRecord::Base
  before_validation :set_initial_position, unless: :has_position?, on: :create

  private

    def has_position?
      !!self.position
    end

    def set_initial_position
      self.position = (self.brothers.maximum("position") || -1) + 1
    end
end

Test:

is_expected.to callback(:set_initial_position).before(:validation).on(:create).unless(:has_position?)

Let me know if this is working for you

justDax commented 8 years ago

Guess I'll change the proc condition with a private method for now, thanks for your time and your suggestions.

thoraxe commented 2 years ago

Should this be closed?