Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The perfect way to validate and test Rails 3 associations (using RSpec/Remarkable)?

I'm still pretty new to testing in Rails 3, and I use RSpec and Remarkable. I read through a lot of posts and some books already, but I'm still kind of stuck in uncertainty when to use the association's name, when its ID.

class Project < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project
end

Because of good practice, I want to protect my attributes from mass assignments:

class Task < ActiveRecord::Base
  attr_accessible :project  # Or is it :project_id??

  belongs_to :project
end

First of all, I want to make sure that a project never exists without a valid task:

class Task < ActiveRecord::Base
  validates :project, :presence => true      # Which one is the...
  validates :project_id, :presence => true   # ...right way to go??
end

I also want to make sure that the assigned project or project ID is always valid:

class Task < ActiveRecord::Base
  validates :project, :associated => true     # Again, which one is...
  validates :project_id, :associated => true  # ...the right way to go?
end

...and do I need the validation on :presence when I use :associated??

Thanks a lot for clarifying, it seems that after hours of reading and trying to test stuff using RSpec/Shoulda/Remarkable I don't see the forest because of all the trees anymore...

like image 708
Joshua Muheim Avatar asked Jul 04 '12 10:07

Joshua Muheim


1 Answers

This seems to be the right way to do it:

attr_accessible :project_id

You don't have to put :project there, too! It's anyway possible to do task.project=(Project.first!)

Then check for the existence of the :project_id using the following (:project_id is also set when task.project=(...) is used):

validates :project_id, :presence => true

Now make sure than an associated Project is valid like this:

validates :project, :associated => true

So:

t = Task.new
t.project_id = 1 # Value is accepted, regardless whether there is a Project with ID 1
t.project = Project.first # Any existing valid project is accepted
t.project = Project.new(:name => 'valid value') # A new valid project is accepted
t.project = Project.new(:name => 'invalid value') # A new invalid (or an existing invalid) project is NOT accepted!

It's a bit a pity that when assigning an ID through t.project_id = it's not checked whether this specific ID really exists. You have to check this using a custom validation or using the Validates Existence GEM.

To test these associations using RSpec with Remarkable matchers, do something like:

describe Task do
  it { should validate_presence_of :project_id }
  it { should validate_associated :project }
end
like image 171
Joshua Muheim Avatar answered Oct 16 '22 15:10

Joshua Muheim