I'm new doing unit test in Ruby on Rails, so I need some help in a test.
This is my model User.rb
class User < ApplicationRecord
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
end
I want to create a test to verify this association. I tried doing this on my user_spec.rb
describe 'should validates associations' do
subject { User.new }
it { should belong_to(subject.created_by) }
end
This is the error response
Failures:
1) User should validates associations should belong to
Failure/Error: it { should belong_to(subject.created_by) }
Expected User to have a belongs_to association called (no association > called )
# ./spec/models/user_spec.rb:17:in `block (3 levels) in '
The ActiveRecord shoulda matchers do not require any object of the class to be instantiated for the tests to run. Here, you have initialised a new User instance as the subject, and have tried to pass that to the shoulda matcher to check the belongs_to association.
However, in order to check a belongs_to association on a model with a particular foreign key and class name, the following test can be used:
it { should belong_to(:created_by).with_foreign_key(:created_by_id).class_name('User') }
ActiveRecord matchers take lots of other options apart from the two mentioned above. These options are very well documented in the Shoulda code on GitHub
You give an instance to matcher but it waits for the reference name and the referenced class name. Your test should be like following.
it { should belong_to(:created_by).of_type(User) }
Related
I have this code in my model.
belongs_to :user, -> { includes :user_profile }
I went through shoulda doc i did not found any way to test that proc
containing includes part. Please help.
Assuming the model in question is named Car (you haven't posted the name of the enclosing class so I'm totally making this up) you can test it as:
describe 'user' do
let(:car) { create_car_somehow }
it 'preloads the user_profile association' do
expect(car.user.association(:user_profile)).to be_loaded
end
end
#association returns metadata of the specified association (of type ActiveRecord::Associations::BelongsToAssociation and so on). The returned object responds to #loaded? which returns true if the association has been loaded and false otherwise.
I have the following setup in my codebase. In such a case what would be the correct way to specify the factories so that we get to keep validation on both the models.
Models:
class LabTest < ActiveRecord::Base
has_many :lab_test_fields
validates_presence_of :lab_test_fields
end
class LabTestField < ActiveRecord::Base
belongs_to :lab_test
validates_presence_of :lab_test
end
Factories:
factory :lab_test do
lab_test_fields FactoryGirl.build_list(:lab_test_field, 5)
end
factory :lab_test_field do
lab_test
end
With this setup if I try to create lab_test or lab_test_field factory by doing FactoryGirl.create(:lab_test) or FactoryGirl.create(:lab_test_field), I get Trait not registered: lab_test (ArgumentError) which is quite unexpected.
From what I can see, you have a typo in the factory. This line...
lab_test_fields FactoryGirl.build_list(:lab_test_field, 5)
...is missing an "=" sign.
So it should be:
lab_test_fields = FactoryGirl.build_list(:lab_test_field, 5).
Also, if you're trying to create 5 lab_test_fields, you want to use create_list not build_list. Create will give you a persisted object, while build gives you an object that isn't persisted. Anyway, that missing "=" sign sounds like its why you're getting that error.
Hope this helps!
Edit: This issue is resolved. The issue was caused by a conflict specific to my application. Another module dynamically created a method named .sources. I was able to troubleshoot by removing the relationship and inspecting the objects method list. Thanks anyways.
I'm using Rails 4.1 with Mongoid 4.0 and have setup a relation as follows:
class Organization
include Mongoid::Document
has_many :sources
end
and
class Source
include Mongoid::Document
belongs_to :organization
end
Then in my rspec test I have:
require 'rails_helper'
RSpec.describe PartnershipsController, :type => :controller do
describe "POST #record" do
it "should create a partnership for the source's organization" do
organization = FactoryGirl.create(:organization)
source = FactoryGirl.create(:source)
organization.sources.push source
end
end
There's more after, but the test fails at the organization.sources.push source line with:
undefined method `push' for nil:NilClass
I don't understand why the error is happening. Looks like in the mongoid documentation that's how I should be adding the related source, but so far no dice. What is the correct way to make this relation?
Edit: adding factory
Here's the organization factory, in case it helps clarify something:
FactoryGirl.define do
factory :organization do
app_name = Faker::App.name
company_name = Faker::Company.name
sequence(:name) { |n| "#{([app_name, company_name].sample)}#{n}" }
defaults_hash = { 'item_type' => 'charity', 'child_item_type' => 'product'}
defaults defaults_hash
end
end
I have an example of code not passing in test but working in the console.
Failing Test:
describe ImporterProfile do
it 'sends defaults method to EventAttribute model' do
expect(ListPage).to receive(:new) #passes
expect(EventAttribute).to receive(:new) #fails
ImporterProfile.new.standard_profile
end
1) ImporterProfile standard_profile sends new method to associated objects
Failure/Error: importer_profile.standard_profile
NoMethodError:
undefined method `each' for nil:NilClass
# ./app/models/importer_profile.rb:51:in `standard_profile'
# ./spec/models/importer_profile_spec.rb:29:in `block (3 levels) in <top (required)>'
Models:
class ImporterProfile < ActiveRecord::Base
has_one :list_page, dependent: :delete
has_many :event_attributes, dependent: :delete_all
accepts_nested_attributes_for :list_page
accepts_nested_attributes_for :event_attributes
def standard_profile
self.list_page = ListPage.new
self.event_attributes = EventAttribute.new
end
end
class EventAttribute < ActiveRecord::Base
belongs_to :importer_profile
end
class ListPage < ActiveRecord::Base
belongs_to :importer_profile
end
However, running this method in the console instantiates a new ImporterProfile, ListPage and several EventAttribute objects.
Anyone understand what is going on here?
I suspect that the problem is that you are mocking EventAttribute.new, but only returning nil, so Rails can't enumerate the active records as is required by the self.event_attributes = statement. (It needs to set the foreign key attribute of the EventAttribute records to the id of the ImporterProfile record.)
If you don't mind continuing with execution, you can do:
expect(EventAttribute).to receive(:new).and_call_original
Alternatively, you can return a double, but you'll need to provide stubs for whatever methods ActiveRecord requires, either by using a library such as http://rubygems.org/gems/rspec-active_record_mocks/versions/0.1.4 or by rolling your own.
As an aside, this question would have been a little easier to answer if you'd provided some way to associate the line numbers in the error stack trace with the sources you provided. Also, the comments on your expect statements that the first passes and the second fails is confusing because it appears that you are raising an error before the expectations are being checked.
Currently, with rspec-rails (2.14.2), I test my associations in model specs with the shoulda (3.5.0) gem like so:
# app/models/user.rb
class User < ActiveRecord::Base
belongs_to :school
end
# spec/models/user_spec.rb
describe User do
it { should belong_to :school }
end
After some research, I hit a wall trying to make association-related assertions work (they all seem to fail).
Error message:
1) User
Failure/Error: it { is_expected.to belong_to :school }
NoMethodError:
undefined method `belong_to' for #<RSpec::ExampleGroups::School:0x007f8a4c7a68d0>
# ./spec/models/user.rb:4:in `block (2 levels) in <top (required)>'
So my questions are:
Can you test associations without the shoulda gem? This doesn't seem possible based on what I've seen with the "expect" syntax.
Does the shoulda gem break with rspec 3.0.1 for everyone? Is there a workaround?
shoulda-matchers is the gem that provides association, validation, and other matchers.
The shoulda gem (which we also make) is for using shoulda-matchers with Test::Unit (and also provides some nice things like contexts and the ability to use strings as test names). But if you're on RSpec, you'll want to use shoulda-matchers, not shoulda.
This is the way it works, now with the shoulda-matchers gem and the "expect" syntax
describe User, type: :model do
it { is_expected.to belong_to :school }
end
I do not believe you need the gem for the basic associations.
You might be having an issues if you haven't actually assigned your user to your school. You need to populate the foreign key, not just use the relationship without having done that.
So you may need to do
#school = School.new
#user = User.new
#school.users << #user
it { should belong_to :school } # within the user block
or
expect(#user).to belong_to #school