I defined these 3 models in Rails3.
class User < ActiveRecord::Base
has_many :questions
has_many :answers
class Question < ActiveRecord::Base
belongs_to :user
has_many :answers
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
I wrote RSpec like this:
describe "user associations" do
before :each do
#answer = #user.answers.build question: #question
end
it "should have the right associated user" do
#answer.user.should_not be_nil
end
it "should have the right associated question" do
#question.should_not be_nil
#answer.question.should_not be_nil #FAIL!!
end
But I always get the following error:
Failures:
1) Answer user associations should have the right associated question
Failure/Error: #answer.question.should_not be_nil
expected: not nil
got: nil
I guess this line is wrong:
#answer = #user.answers.build question: #question
But how should I build answer object?
Update: Thanks everyone, I found I should have to write like this:
require 'spec_helper'
describe Answer do
before :each do
#user = Factory :user
asker = Factory :user, :user_name => 'someone'
#question = Factory :question, :user => asker
end
describe "user associations" do
before :each do
#answer = Factory :answer, :user => #user, :question => #question
end
it "should have the right associated user" do
#answer.user.should_not be_nil
end
it "should have the right associated question" do
#answer.question.should_not be_nil
end
end
end
Here is spec/factories.rb:
Factory.define :user do |user|
user.user_name "junichiito"
end
Factory.define :question do |question|
question.title "my question"
question.content "How old are you?"
question.association :user
end
Factory.define :answer do |answer|
answer.content "I am thirteen."
answer.association :user
answer.association :question
end
Once I explicitly save the #user instance, the spec doesn't fail anymore. Here's my version:
require 'spec_helper'
describe Answer do
before :each do
#user = User.new
#user.save!
#question = #user.questions.build
#question.save!
#answer = #user.answers.build question: #question
#question.answers << #answer
end
it "should have the right associated user" do
#answer.user.should_not be_nil
end
it "should have the right associated question" do
#question.should_not be_nil
#answer.question.should_not be_nil # SUCCESS!
end
end
Related
I am currently building very simple Comment system on Rails. The primary models are User, Albumpost, and Comment. Users can post Albumposts. For each Albumpost, Users can add Comments to the Albumpost. As a result, a Comment belongs to a User and belongs to an Albumpost.
The problem I'm having is that even with the proper associations in my models (see below), I can't get
#comment.user.name
when I'm trying to render the comments in the albumpost 'show' page (/views/albumposts/show.html.erb). When I go to the page, I can't get #comment.user.name (doesn't understand the association) and get a
"undefined method `name' for nil:NilClass"
Oddly I can get
#comment.albumpost.content
I've double-checked my models and also added the proper foreign keys to the models. Am I doing something wrong in the controllers?
Here are my models:
class Comment < ActiveRecord::Base
attr_accessible :body, :albumpost_id, :user_id
belongs_to :albumpost
belongs_to :user
end
class Albumpost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
has_many :comments, dependent: :destroy
end
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :albumposts, dependent: :destroy
has_many :comments, dependent: :destroy
end
Here are the relevant parts of my Albumpost and Comments controllers:
class AlbumpostsController < ApplicationController
def show
#albumpost = Albumpost.find(params[:id])
#comments = #albumpost.comments
#comment = Comment.new
#comment.albumpost_id = #albumpost.id
#comment.user_id = current_user.id
end
end
class CommentsController < ApplicationController
def create
albumpost_id = params[:comment].delete(:albumpost_id)
#comment = Comment.new(params[:comment])
#comment.albumpost_id = albumpost_id
#comment.user_id = current_user.id
#comment.save
redirect_to albumpost_path(#comment.albumpost)
end
end
I think you should prefer setting objects to relations instead of setting their ids. For example, you should do this:
#comment.user = current_user
instead of
#comment.user_id = current_user.id
ActiveRecord will take care of setting corresponding *_id fields. I'm not sure how it handles the reverse. (it should autoload though, if I understand correctly)
I have a model called DefaultCompany that has no controller, instead I create it through the companies_controller which calls the user.set_default_company (defined below) if they check the "default company" checkbox on the form.
Default company is a joining table of user_id and company_id.
class DefaultCompany < ActiveRecord::Base
attr_accessible :company_id, :user_id
belongs_to :company
belongs_to :user
end
I keep getting the following error:
Can't mass-assign protected attributes: company, user
app/models/user.rb:22:in `set_default_company'
app/controllers/companies_controller.rb:23:in `create'
I've set my user model to be able to accept nested attributes for DefaultCompany, like this
class User < ActiveRecord::Base
has_one :default_company
accepts_nested_attributes_for :default_company
attr_accessible :default_company_attributes
def set_default_company(company)
exists = DefaultCompany.find(self.id)
if exists
exists.update_attributes(company: company)
else
DefaultCompany.create(company: company, user: self)
end
end
end
And here is the create action for the companies_controller.rb
def create
#company = Company.new(params[:company])
if #company.save
if params[:default_company]
current_user.set_default_company #company.id
end
flash[:notice] = "Company was successfully created."
Role.assign_creator(#company.id, current_user.id)
redirect_to #company
else
redirect_to new_company_path
end
end
So I'm not sure what I need to add so that mass-assignment will pass, can anyone help me figure out / explain this?
I believe rails is strict about the naming in mass-assignment, so although you've whitelisted company_id and user_id, you have not whitelisted company and user.
Try changing the assignment in set_default_company to:
if exists
exists.update_attributes(company_id: company.id)
else
DefaultCompany.create(company_id: company.id, user_id: self.id)
end
You can either change the attr_accessible attributes on Company to :user and :company or set :company_id and :user_id in your set_default_company method call.
Edit:
exists = DefaultCompany.find(self.id)
This seems to be wrong according to your logic.
I would like to do testing my rails application, especially on controller, here are the code
customer_controller_test.rb
require 'test_helper'
class CustomersControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup do
#user = FactoryGirl.create(:user)
#role = FactoryGirl.create(:role)
#puts #role.name
#permission = FactoryGirl.create(:permission)
#puts #permission.name
#role_permission = FactoryGirl.create(:role_permission)
#puts #role_permission.role_id
#puts #role_permission.permission_id
sign_in #user
#customer = FactoryGirl.create(:customer)
end
test "should get index" do
get :index
assert_response :success
assert_template 'index'
assert_not_nil assigns(:customers)
end
test "should show article" do
get :show, :id => #customer.to_param
assert_response :success
assert_template 'show'
assert_not_nil assigns(:customer)
assert assigns(:customer).valid?
end
test "should get new" do
#login_as(#user)
get :new
assert_response :success
end
end
factories.rb <-- setup tha fixtures with factorygirl
FactoryGirl.define do
sequence :email do |n| "admin#admin.admin#{n}" end
sequence :role_name do |n| n end
sequence :role_id do |n| n end
sequence :permission_id do |n| n end
factory :user do |u|
u.name "Admin"
u.role_id {1}
u.email do FactoryGirl.generate(:email) end
u.password "123456"
u.after(:create) do |user|
user.creator_id {1}
user.save
end
end
factory :customer do
name "Test customer-name"
code "Test customer-code"
address "Test customer-address"
phone "Test customer phone"
end
factory :permission do
name "Customer"
end
factory :role do
name do FactoryGirl.generate(:role_name) end
end
end
And I got error
Any idea? Thx before
Try creating the role in FactoryGirl before assigning it to the user. It looks like you've got a validation error when saving your User record, because it's trying to validate that you've got an actual role assigned. You don't, because you're attempting to create your role after your user.
Just try switching the two lines in your FactoryGirl "setup" like this:
#role = FactoryGirl.create(:role)
#user = FactoryGirl.create(:user)
Or, check out this link for a more thorough examination of how to use FactoryGirl to test associations:
http://blog.joshsoftware.com/2011/05/13/testing-associations-with-factory-girl/
I may have my associations messed up. I have the following models: User and UserProfiles.
My models:
class User < ActiveRecord::Base
has_one :user_profile, :dependent => :destroy
attr_accessible :email
end
class UserProfile < ActiveRecord::Base
belongs_to :user
end
I have a column named "user_id" in my user_profiles table.
My factory is setup like so:
Factory.define :user do |user|
user.email "test#test.com"
end
Factory.sequence :email do |n|
"person-#{n}#example.com"
end
Factory.define :user_profile do |user_profile|
user_profile.address_line_1 "123 Test St"
user_profile.city "Atlanta"
user_profile.state "GA"
user_profile.zip_code "30309"
user_profile.association :user
end
My user_spec test is setup like so:
describe "profile" do
before(:each) do
#user = User.create(#attr)
#profile = Factory(:user_profile, :user => #user, :created_at => 1.day.ago)
end
it "should have a user profile attribute" do
#user.should respond_to(:user_profile)
end
it "should have the right user profile" do
#user.user_profile.should == #profile
end
it "should destroy associated profile" do
#user.destroy
[#profile].each do |user_profile|
lambda do
UserProfile.find(user_profile)
end.should raise_error(ActiveRecord::RecordNotFound)
end
end
end
My user_profile_spec is setup like so:
describe UserProfile do
before(:each) do
#user = Factory(:user)
#attr = { :state => "GA" }
end
it "should create a new instance with valid attributes" do
#user.user_profiles.create!(#attr)
end
describe "user associations" do
before(:each) do
#user_profile = #user.user_profiles.create(#attr)
end
it "should have a user attribute" do
#user_profile.should respond_to(:user)
end
it "should have the right associated user" do
#user_profile.user_id.should == #user.id
#user_profile.user.should == #user
end
end
end
When I run the tests I get "undefined method `user_profiles' for #". How is my test flawed or is my relationship flawed?
Thanks!
You have a has_one association called user_profile (singular). You do not have an association called user_profiles (plural).
What I have now:
class User < ActiveRecord::Base
has_many :people
end
... and...
class Person < ActiveRecord::Base
belongs_to :user
end
In spec/factories.rb:
Factory.define :user do |u|
u.email "test#test.com"
u.password "testpassword"
u.password_confirmation "testpassword"
u.display_name "neezer"
# u.people { |i| [i.association(:person)] }
end
Factory.define :person do |p|
p.first_name "p_firstname"
p.last_name "p_lastname"
p.gender "male"
p.association :user
end
I want to setup the user factory to create with 1 person association, but if I uncomment that line, when I run my tests, my system hangs for quite some time, before outputting this failure:
1) User can be created from a factory
Failure/Error: Unable to find matching line from backtrace
SystemStackError:
stack level too deep
# /Users/test/.rvm/gems/ruby-1.9.2-p0/gems/activerecord-3.0.5/lib/active_record/persistence.rb:285
What am I doing wrong here? I would like to have tests that require an association between these two models, such that (1) a User must have at least 1 person, and (2) a Person must belong to a User.
Is this a first-priority issue? I'll admit I'm a bit lost here...
I'm using rspec 2.5.0, factory_girl_rails 1.0.1, and rails 3.0.5.
My specs:
user_spec.rb:
require 'spec_helper'
describe User do
subject { Factory :user }
# ...
context "has associations, " do
it "can have people" do
subject.should respond_to :people
end
it "must have at least 1 person" do
subject.send "people=", nil
subject.should_not be_valid
subject.errors[:people].should_not be_empty
end
end
end
person_spec.rb:
require 'spec_helper'
describe Person do
subject { Factory :person }
# ...
context "has validation, " do
[:gender, :user].each do |attr|
it "must have a #{ attr }" do
subject.send "#{attr}=", nil
subject.should_not be_valid
subject.errors[attr].should_not be_empty
end
end
end
context "has associations, " do
it "can have a User" do
subject.should respond_to :user
end
end
end
Keep that line but remove p.association :user from your person factory.
I've since discovered Shoulda, which provides a nice rspec matchers like these:
subject.should belong_to :user
subject.should have_many :people
Which has solved my issue.