dodgerogers747
dodgerogers747

Reputation: 3345

2 Rspec tests fail due to nil:NillCLass error in model spec when others pass

I have the following Rspec test for a vote model, which includes a custom validation ensuring you can't vote on your own content, which is shown below. I am puzzled as to why only 2 of these tests fail with a nilclass error when the other tests within the spec pass.

@vote must be nil but why aren't the other tests failing with the same error?

vote.rb

validates :ensure_not_author

def ensure_not_author 
    votable = self.votable_type.downcase
    errors.add(:user_id, "You can't vote on your own content.") if self.votable.user_id == self.user_id
  end

factories

factory :answer do
    user_id :user
    question_id :question
    body "you need to change your grip"
    votes_count 0
    correct false
  end

  factory :vote do
    user_id :user
    votable_id :answer
    votable_type "Answer"
    value 1
    points 5
  end

factory :user do |u|
    u.sequence(:email) {|n| "test#{n}@hotmail.com"}
    u.sequence(:username) {|n| "tester#{n}" }
    u.password "password"
    u.password_confirmation "password" 
    u.remember_me true
    u.reputation 200
  end

vote_spec.rb

require "spec_helper"

describe Vote do
  before(:each) do
    @user2 = FactoryGirl.create(:user)
    @user = FactoryGirl.create(:user)
    @answer = FactoryGirl.create(:answer, user_id: @user)
    @vote = Vote.create(user_id: @user2.id, value: 1, points: 5, votable_id: @answer.id, votable_type: "Answer")
  end

  subject { @vote }

  it { should respond_to(:user_id) }
  it { should respond_to(:votable_id) }
  it { should respond_to(:votable_type) }
  it { should respond_to(:value) }
  it { should respond_to(:points) }

   describe 'value' do
     before { @vote.value = nil }
     it { should_not be_valid }
   end

   describe "user_id" do
      before { @vote.user_id = nil }
       it { should_not be_valid }
     end

   describe "votable_id" do
     before { @vote.votable_id = nil }
     it { should_not be_valid }
   end

   describe "votable type" do
      before { @vote.votable_type = nil }
      it { should_not be_valid }
   end

   describe "vote value" do
      before { @vote.value = 5 }
      it { should_not be_valid }
   end
end

Failures:

1) Vote votable_id 
     Failure/Error: it { should_not be_valid }
     NoMethodError:
       undefined method `user_id' for nil:NilClass
     # ./app/models/vote.rb:17:in `ensure_not_author'
     # ./spec/models/vote_spec.rb:25:in `block (3 levels) in <top (required)>'

  2) Vote votable type 
     Failure/Error: it { should_not be_valid }
     NoMethodError:
       undefined method `downcase' for nil:NilClass
     # ./app/models/vote.rb:16:in `ensure_not_author'
     # ./spec/models/vote_spec.rb:35:in `block (3 levels) in <top (required)>'

Upvotes: 1

Views: 273

Answers (1)

Arie Xiao
Arie Xiao

Reputation: 14082

You validator ensure_not_author depends Vote#votable_type and Vote#votable to function well. And when you test validity of @vote, this validator will be tested.

However, in your "votable_id" testcase, you set votable_id to be nil. Later when you test @vote's validity with should_not be_valid, the ensure_not_author is called and failed at self.votable.user_id because ActiveRecord will query for Votable with votable_id.

Similarly, your "votable type" test case failed at self.votable_type.downcase since you set votable_type to be nil.

You should check the availability of the attributes in your validator before you send messages to them. Or write other validators to check them before ensure_not_author.

Upvotes: 2

Related Questions