Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec2 testing a before_validation method

I have the following to remove the spaces on a specific attribute.

#before_validation :strip_whitespace

protected
  def strip_whitespace
    self.title = self.title.strip
  end

And I want to test it. For now, I've tried:

it "shouldn't create a new part with title beggining with space" do
   @part = Part.new(@attr.merge(:title => " Test"))
   @part.title.should.eql?("Test")
end

What am I missing?

like image 433
Kleber S. Avatar asked Sep 09 '11 11:09

Kleber S.


2 Answers

Validations won't run until the object is saved, or you invoke valid? manually. Your before_validation callback isn't being run in your current example because your validations are never checked. In your test I would suggest that you run @part.valid? before checking that the title is changed to what you expect it to be.

app/models/part.rb

class Part < ActiveRecord::Base
  before_validation :strip_whitespace

protected
  def strip_whitespace
    self.title = self.title.strip
  end
end

spec/models/part_spec.rb

require 'spec_helper'

describe Part do
  it "should remove extra space when validated" do
    part = Part.new(:title => " Test")
    part.valid?
    part.title.should == "Test"
  end
end

This will pass when the validation is included, and fails when the validation is commented out.

like image 137
danivovich Avatar answered Sep 20 '22 16:09

danivovich


referring to @danivovich example

class Part < ActiveRecord::Base
  before_validation :strip_whitespace

protected
  def strip_whitespace
    self.title = self.title.strip
  end
end

the proper way to write the spec is to separately write spec on strip_whitespace method and then just check if model class have callback set on it, like this:.

describe Part do
  let(:record){ described_class.new }

  it{ described_class._validation_callbacks.select{|cb| cb.kind.eql?(:before)}.collect(&:filter).should include(:strip_whitespace) }

  #private methods
  describe :strip_whitespace do
    subject{ record.send(:strip_whitespace)} # I'm using send() because calling private method
    before{  record.stub(:title).and_return('    foo    ')
    it "should strip white spaces" do
      subject.should eq 'foo'
      # or even shorter
      should eq 'foo'
    end
  end
end

if you need to skip callback behavior in some scenarios use

before{ Part.skip_callback(:validation, :before, :strip_whitespace)}
before{ Part.set_callback( :validation, :before, :strip_whitespace)}

Update 20.01.2013

BTW I wrote a gem with RSpec matchers to test this https://github.com/equivalent/shoulda-matchers-callbacks

In general I don't recommend callback at all. They are fine for example in this question, however once you do more complicated stuff like:

After create->

  • link account to user
  • create notification
  • send email to Admin

...then you should create custom service object to deal with this and test them separately.

  • http://railscasts.com/episodes/398-service-objects
  • http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
like image 41
equivalent8 Avatar answered Sep 23 '22 16:09

equivalent8