I'm writing RSpec code for polymorphic associations. I have two testing ideas that
Use Factory Girl to build polymorphic associations
Use rails methods to build polymorphic associations.
Here are the pieces of the code I wrote (relevant codes are at the bottom):
1) link_spec.rb, creating the association with FactoryGirl.
describe "Book links" do
let(:book) { FactoryGirl.create(:book) }
let(:book_link) { FactoryGirl.create(:book_link, linkable: book) }
subject{ book_link }
it { should be_valid }
it { should respond_to(:url) }
it { should respond_to(:linkable) }
its(:linkable) { should eq book }
end
2) link_spec.rb, creating the association through rails methods.
describe "Book links" do
let(:book) { FactoryGirl.create(:book) }
let(:link) { book.links.create(url: "http://example.com") }
subject { link }
it { should be_valid }
it { should respond_to(:url) }
it { should respond_to(:linkable) }
its(:linkable) { should eq book }
end
I feel the latter is testing better than the former, but have no confidence.
Or are they equivalent to each other?
book.rb
class Book < ActiveRecord::Base
has_many :links, as: :linkable
end
link.rb
class Link < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
factories.rb
factory :book do
title "Foo bar"
author "John"
end
factory :book_link, class: "Link" do
association :linkable, factory: :book
url "http://examle.com"
end
I feel the latter is testing better than the former, but have no
confidence.
If you intended to implicitly ask which was "better", then that question is not a good fit for StackOverflow.
Or are they equivalent to each other?
In the sense that the setup results in the same Link object being created, I would say that yes, they are equivalent. However, the second tests the association helper method set up for Book, which you may find desirable.
Related
My model has a validation concerning one of it's associated models. My rspec tests of the model are failing due to this validation.
describe NewOfferRange do
it { is_expected.to validate_presence_of(:new_offer) }
it { is_expected.to validate_presence_of(:from) }
it { is_expected.to validate_presence_of(:to) }
end
class NewOfferRange < ApplicationRecord
validates :new_offer, :from, :to, presence: true
validate :unique_for_date_ranges
delegate :hotel, to: :new_offer
def unique_for_date_ranges
if hotel.new_offers.joins(:new_offer_ranges)
.where('new_offer_ranges.from < ? AND new_offer_ranges.to > ?', to, from)
.where.not(new_offer_ranges: { id: id })
.count
.positive?
errors.add(:base, 'Ya hay otra oferta para esas fechas.')
end
end
Those test fail because when trying to run the validation, it says new_offer is nil for the new_offer_range.
I'm also using FactoryGirl for my model factories which has defined a factory for new_offer_range that is valid and has the corresponding references.
Can I somehow tell should matchers to use that factory so as not to get this validation error?
Setting
subject { create :new_offer_range }
before it-blocks should solve the problem. It will set the subject of testing, not a default object, but one that you want to have.
I know very little about FactoryGirl, and have only ever created single-record factories with no associations. Here are two factories I have now for associated models:
factory :help_request do
name "Mrs. Bourque's holiday party"
description "We need volunteers to help out with Mrs. Bourque's holiday party."
end
factory :donation_item do
name "20 Cookies"
end
Whenever I've needed to associate two records I do it in rspec after the fact with code like this:
require 'spec_helper'
describe HelpRequest do
let(:help_request) { FactoryGirl.create(:help_request) }
let(:donation_item) { FactoryGirl.create(:donation_item) }
subject { help_request }
before {
donation_item.help_request_id = help_request.id
donation_item.save!
}
Ordinarily this has worked, but now I validate that there is at least one donation_item not already marked for destruction:
class HelpRequest < ActiveRecord::Base has_many :events
has_many :donation_items, dependent: :destroy
validate :check_donation_items?
def has_donation_items?
self.donation_items.collect { |i| !i.marked_for_destruction? }.any?
end
def check_donation_items?
if !has_donation_items?
errors.add :a_help_request, "must have at least one item."
end
end
When I run my model test, everything fails with the following:
Failure/Error: let(:help_request) { FactoryGirl.create(:help_request) }
ActiveRecord::RecordInvalid:
Validation failed: A help request must have at least one item.
How can I associate the donation item right in the factory at the time the help_request gets created? I see other answers that seem related, but because my understanding of FactoryGirl is so rudimentary, I can't figure out how to make it work.
factory :donation_item do
name "20 Cookies"
help_request
end
factory :help_request do
name "Mrs. Bourque's holiday party"
description "We need volunteers to help out with Mrs. Bourque's holiday party."
end
Then in your spec:
let(:donation_item) { FactoryGirl.create(:donation_item, help_request: FactoryGirl.create(:help_request)) }
Edit
Do not include help_request assocation in the :donation_item factory, and do this in your test:
let(:help_request) do
help_request = build(:help_request)
help_request.donation_items << build(:donation_item)
help_request.save!
help_request
end
subject { help_request }
I am trying to test the following scenario:
-> I have a model called Team which it just makes sense when it has been created by a User. Therefore, each Team instance has to be related to a User.
In order to test that, I have done the following:
describe Team do
...
it "should be associated with a user" do
no_user_team = Team.new(:user => nil)
no_user_team.should_not be_valid
end
...
end
Which forces me to change the Team model as:
class Team < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :user
validates_presence_of :name
validates_presence_of :user
belongs_to :user
end
Does this seem correct to you? I am just worried of make the :user attribute as accessible (mass assignment).
I usually use this approach:
describe User do
it "should have many teams" do
t = User.reflect_on_association(:teams)
expect(t.macro).to eq(:has_many)
end
end
A better solution would be to use the gem shoulda which will allow you to simply:
describe Team do
it { should belong_to(:user) }
end
it { Idea.reflect_on_association(:person).macro.should eq(:belongs_to) }
it { Idea.reflect_on_association(:company).macro.should eq(:belongs_to) }
it { Idea.reflect_on_association(:votes).macro.should eq(:has_many) }
class MicroProxy < ActiveRecord::Base
has_many :servers
end
describe MicroProxy, type: :model do
it { expect(described_class.reflect_on_association(:servers).macro).to eq(:has_many) }
end
RSpec is a ruby test framework, and not a rails framework.
belongs_to is a rails construct, and not a ruby construct.
Gems like shoulda-matchers connect ruby and rails things and help you write good tests.
Having the above in mind and following official documentation, should help you stay up to date and understand what you are writing.
So, below is what I would write.
User model:
RSpec.describe User, type: :model do
context 'associations' do
it { should have_many(:teams).class_name('Team') }
end
end
Team model:
RSpec.describe Team, type: :model do
context 'associations' do
it { should belong_to(:user).class_name('User') }
end
end
I have many active record validations which work ok, such as this:
code: belongs_to :topics
test: it { should belong_to :topic }
However I have an active_hash association and a test like this:
code: belongs_to_active_hash :day
test: pending { should belong_to_active_hash :day }
but the tests fails:
undefined method if' for #<Shoulda::Matchers...
Shoulda doesn't have support for ActiveHash matchers. You can test this manually:
specify do
reflection = YourModel.reflect_on_association(:day)
options = {} # options you passed to belongs_to_active_hash
reflection.should_not be_nil
reflection.macro.should eq :belongs_to
reflection.options.should eq options
end
If you use this often, you can create a custom matcher.
I'm using rspec and I'm trying to test whether or not my model y has many x. I've tried all sorts of things, including looping through the methods array, and can't seem to find a good method online. So what should I use?
Without much hacking you could use remarkable gem: http://github.com/carlosbrando/remarkable
Taken from remarkable docs:
describe Post do
it { should belong_to(:user) }
it { should have_many(:comments) }
it { should have_and_belong_to_many(:tags) }
end
You can reflect on the class:
MyModel.reflect_on_association(:x).macro == :has_one
It's probably easier if you just use Shoulda, there are helper methods so it reads much more cleanly: it { should have_many(:x) }
here's an rspec independent solution, the key is to use reflect_on_assocation
class MyModel < ActiveRecord::Base
has_many :children
belongs_to :owner
end
reflection_children = MyModel.reflect_on_association(:children)
if !reflection_children.nil?
if reflection_children.macro == :has_many
# everything is alright
else
# it's not has_many but exists
end
else
# it doesn't exist at all !
end
reflection_owner = MyModel.reflect_on_association(:owner)
if !reflection_owner.nil?
if reflection_owner.macro == :belongs_to
# everything is alright!
else
# it's not belongs_to but exists
end
else
# it doesn't exist at all!
end