Bart
Bart

Reputation: 294

Accessing model's fields

I'm making my first steps with rails and am reading M.Hartl's tutorial. I'm trying to write some simple test to check that following relationship is deleted when we delete user

describe "deleting user should delete relationship" do
  before do
    other_user.destroy
  end

  expect(@user.name).to eq("Tom") # undefined method name
  expect(@user.followers).to include(other_user) # undefined method followers
  its(:followers) { should_not include(other_user) } # this works
end

why @user.name and @user.followers are not defined? how can I access it? I'd expect at least name to be there as it's property of user model... is it somehow private and I need to write accessor to access it? Same for followers.. do I have to write separate method to return user's followers?

User model looks like this:

class User < ActiveRecord::Base
  has_many :microposts, dependent: :destroy
  has_many :relationships, foreign_key: "follower_id", dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :reverse_relationships, foreign_key: "followed_id", class_name: "Relationship", dependent: :destroy
  has_many :followers, through: :reverse_relationships, source: :follower # we could omit source in this case since, in the case of a :followers attribute, Rails will singularize “followers” and automatically look for the foreign key follower_id in this case.
  ...

Upvotes: 0

Views: 161

Answers (2)

Gil Gomes
Gil Gomes

Reputation: 96

You have to mock your object @user. The easy way is

let(@user){ User.new(name: "Foo") }

This instanciate a new User to @user. I prefer to use FactoryGirl for do that. But User.new will solve your problem

Upvotes: 0

BroiSatse
BroiSatse

Reputation: 44675

You can't use expectation outside of tests - you need to wrap them in:

it 'name is Tom' do
  expect(@user.name).to eq 'Tom'
end

it 'is followed by deleted user' do
  expect(@user.followers).to include(other_user)
end

its method is a handy shortcut:

its(:attribute) { should be_present }    # test name: Its attribute should be present

is the same as (except for the better test name):

it 'has present attribute' do            # test name: It has present attribute
  subject.attribute.should be_present
end

Note that you need to have subject defined in your test suite to use the second form. If you have subject defined, you should use it instead of instance variables (so you can easily change the subject without altering all your tests). So your tests should rather look like this:

its(:name) { should eq 'Tom' }
its(:followers) { should_not include(other_user) }

The reason why it is not working the way you wrote it is that @user is an instance variable, which you are trying to execute in the context of class. This naturally returns 'nil', since the class did not define such variable.

Upvotes: 3

Related Questions