Rspec - unknown attribute error - ruby-on-rails

I followed the Restauranlty tutorial at rubyonrailstutor.com and everything worked perfectly. Now I'm trying to implement the same tests in my project, which has more models with associations between them and I'm getting errors.
I've tried researching this problem, and even came across a few questions on this site - most notably this one and this one.
I have the following models:
class Album < ActiveRecord::Base
belongs_to :artist
validates_presence_of :title
validates_presence_of :no_of_tracks
end
class Artist < ActiveRecord::Base
has_many :albums
end
And I have the following factories:
FactoryGirl.define do
factory :artist do
name "MyString"
image "MyString"
bio "MyString"
end
end
FactoryGirl.define do
factory :album do
title "MyString"
no_of_tracks 0
artist
end
end
Apart from following the Restaurantly tutorial, this is my first time testing so I also looked at this guide for setting up factory girl for models with associations.
My test is:
describe Album do
subject(:album) { FactoryGirl.build :album, title: nil, artist: nil,
no_of_tracks: nil }
it { expect(album.valid?).to be_false }
end
But when I run it it fails with the error: can't write unknown attribute artist_id.
The questions I mentioned above suggested that perhaps my database has not been properly migrated but I've run both
bundle exec rake db:migrate RAILS_ENV=test
and
bundle exec rake db:test:prepare
to no avail. As I said, I'm very, very new to testing so if anyone could point me in the right direction I'd be grateful.

Everything looks fine.
Do you really have a column artist_id in your albums table ?

I suspect that albums table in your database does not have artist_id foregin key.

Related

Trying to understand why validates_presence_of test fails

I'm just starting to have a play with Rails (using Rspec and Shoulda Matchers) to build a demo blog.
I've literally just started and my first test is failing but I can't understand why.
I think I've set up everything correctly, but when I try to validate that a title is present on my Article model but it returns a failure
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
The matcher attempted to set :title on the Article to nil, but that
attribute does not exist.
My model looks like this...
class Article < ApplicationRecord
# attr_accessor :title
validates_presence_of :title
end
and my test...
require 'rails_helper'
RSpec.describe Article do
it { should validate_presence_of :title }
end
If I uncomment out the attr_accessor then the test passes but I understand that it's not required with Rails.
What am I doing wrong?
The attr_accessor is not required, as long as your articles database table has a title column.
You can check that in db/schema.rb.

Mongoid relation throwing NoMethodError: undefined method 'push' for nil:NilClass

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

testing models with builds or matcher in factory girl

I have the following factory for patient_allergies
FactoryGirl.define do
factory :patient_allergy do
patient
name 'Peanuts'
end
end
The following factory for patient_allergy_reactions
FactoryGirl.define do
factory :patient_allergy_reaction do
patient_allergy
name 'Fever'
severity 'High'
end
end
The model for patient_allergy looks like this:
class PatientAllergy < ActiveRecord::Base
belongs_to :patient
has_many :patient_allergy_reactions
end
the model for patient_allergy_reaction looks like this:
class PatientAllergyReaction < ActiveRecord::Base
belongs_to :patient_allergy
end
My model tests look this:
it 'returns correct allergies with reactions' do
#create an allergy
allergy_name = 'Peanuts'
patient_allergy = create(:patient_allergy, name: allergy_name, patient: patient)
#create a allergy reaction
reaction_name = 'Fever'
reaction_severity = 'Low'
allergy_reaction = create(:patient_allergy_reaction, name: reaction_name, severity: reaction_severity, patient_allergy: patient_allergy)
expect(patient.patient_allergies.size).to eq(1)
expect(patient.patient_allergies[0]).to eq(patient_allergy)
expect(patient.patient_allergies[0].patient_allergy_reactions[0]).to eq(allergy_reaction)
end
The above works fine but doesnt seem to add much value.
I am trying to figure out a way to use build and traits for the above test.
Else, is there a way to use the expect(patient).to have_many(:patient_allergies) matcher or something.
It would be really helpful if i could understand testing my models with factory girl.
The above works fine but doesnt seem to add much value
Agreed. Your model specs should test methods that you write, instead of testing the behavior of Rails.
If you want to test your associations, you can check out shoulda-matchers, which has standard tests for Rails models.

rails 3.1 inflection problem

I have a Rails 3.1 app with the following 2 models
class Listing < ActiveRecord::Base
has_many :listing_saves
end
class Team < ActiveRecord::Base
has_many :listing_saves
has_many :saved_listings, through: :listing_saves, source: 'listing'
end
The Join model looks like this
class ListingSave < ActiveRecord::Base
belongs_to :team
belongs_to :listing
end
Mow I think that there is an inflection problem because whenever I try to run my tests I get the following error (this is an example of an error and the test that caused it)
it "should return the listing saves associated with the team" do
save = Factory :listing_save, listing: #listing, saver: #user, team: #team
#team.listing_saves.should include save
end
Failures:
1) Team listing_saves associations should return the listing saves associated with the team
Failure/Error: #team.listing_saves.should include save
NameError:
uninitialized constant Team::ListingSafe
# ./spec/models/team_spec.rb:55:in `block (3 levels) in <top (required)>'
as if Rails is singularizing listing_saves into listing_safe
Here are some custom inflectors I have tried (not all at the same time) (none of them work)
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
inflect.plural 'saved_listing', 'saved_listings'
inflect.singular 'saved_listings', 'saved_listing'
inflect.plural 'listing_save', 'listing_saves'
inflect.singular 'listing_saves', 'listing_save'
inflect.singular 'listing_safes', 'listing_safe'
inflect.plural 'listing_safe', 'listing_safes'
inflect.irregular 'listing_save', 'listing_saves'
inflect.irregular 'saved_listing', 'saved_listings'
end
What can I do next?
Note: I found the this similar question but the answer doesn't seem to solve my problem
Edit
I followed the answer below so that I now have the following in my config/initializers/inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'listing_save', 'listing_saves'
end
When I open up a console session and run "listing saves".singularize I get "listing_save" as I would hope. However, it seems that at least part of my application doesn't get it, my tests still fail in the same way as before. (I swear I'm restarting my server and spork before I test/run the application!).
Edit 2
I wrote some tests for inflections in my app:
describe "inflection" do
it "should singularize listing_saves properly" do
"listing_saves".singularize.should == "listing_save"
end
it "should pluralize listing_save properly" do
"listing_save".pluralize.should == "listing_saves"
end
end
Now I have a situation where these tests pass fine, but other tests still fail with the same error I was having before
NameError:
uninitialized constant User::ListingSafe
Same app, same spork instance, same files loaded. Something weird is going on here!??
You need to define an irregular inflection:
# Test your inflections!
> "listing_save".pluralize
=> "listing_saves" # OK!
> "listing_saves".singularize
=> "listing_safe" # Ouch :(
# Make it smarter
ActiveSupport::Inflector.inflections { |i|
i.irregular 'listing_save', 'listing_saves'
}
# Test again
> "listing_saves".singularize
=> "listing_save" # Yay!
Ruby docs:
------------------------ ActiveSupport::Inflector::Inflections#irregular
irregular(singular, plural)
------------------------------------------------------------------------
Specifies a new irregular that applies to both pluralization and
singularization at the same time. This can only be used for
strings, not regular expressions. You simply pass the irregular in
singular and plural form.
Examples:
irregular 'octopus', 'octopi'
irregular 'person', 'people'
Edit:
Some further investigation - and it looks like others have stumbled upon this same problem also (inflections not working as expected with associations). So in the meantime you can set the class name manually:
has_many :listing_saves, :class_name => "ListingSave"
Someone else with the same problem, and an additional inflections tweak. Personally I'd go with the :class_name setting instead though:
Issue with custom inflections in Ruby on Rails 3.0.3

Getting a NameError with ActiveRecord and relationships

I've run into a problem when using a one to many relationship. I want to have each Series have one Publisher and that one Publisher has many Series.
This is my Publisher model:
class Publisher < ActiveRecord::Base
validates_presence_of :name
has_many :series
end
This is my Serie model:
class Serie < ActiveRecord::Base
belongs_to :publisher
end
This is the failing test:
test "a publisher should have a list of series" do
#publisher = Publisher.new :name => "Standaard Uitgeverij"
#series = [ Serie.new(:name => "De avonturen van Urbanus", :publisher => #publisher),
Serie.new(:name => "Suske en Wiske", :publisher => #publisher) ]
assert_equal #series, #publisher.series
end
The test fails on the last line with NameError: uninitialized constant Publisher::Series.
I tried to save the publisher and the series, but this did not work. I tried it with only one serie, but this gives the same error.
Since I'm just starting out with Rails and Ruby, I am at a loss here. What am I doing wrong?
To address your actual question as mentioned in your comment (how can I name my model "Series"?), you need to make the Rails' Inflector aware of this exception to its default pluralization rules.
Add the following to config/environment.rb:
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable 'series'
end
This will let you name your model as Series. You can test that it's worked using script/console:
>> "series".pluralize #=> "series"
>> "series".singularize #=> "series"
—I have to say that I've just tried using The Pluralizer and it would appear that Rails has knowledge of how to handle the word series built-in. Try it for yourself.
I believe John's answer is the best one.
You can also directly specify the class name in the has_many declaration
has_many :series, :class_name => 'Serie'
Your has_many relationship name is fine, but your model name is wrong.
As the singular and plural of series are both series, you need to rename your model from Serie to Series. After that, everything should be fine.

Resources