Reputation: 169
For some reason my Rspec Model tests are failing for one of my models due to a custom validation method that is being called, but not found within the test. At least that's what I think is happening. This validation is triggered when the form is submitted to create a new Dinosaur in the app. Can anyone tell me why this may be happening and what a potential fix may be?
This is the failure error for all 4 tests:
Failure/Error: if cage.at_capacity?
NoMethodError:
undefined method `at_capacity?' for nil:NilClass
Model/dinosaur.rb
class Dinosaur < ApplicationRecord
belongs_to :cage
validates :name, :species, :diet_type, :cage_id, presence: true
validates_uniqueness_of :name
validate :is_cage_at_capacity
validate :is_cage_powered_down
validate :cage_contains_diet_mismatch
=begin
def set_cage(c)
return false if c.at_capacity?
cage = c
end
def move_dino_to_powered_down_cage(c)
return false if c.is_powered_down?
cage = c
end
=end
def is_herbivore?
return diet_type == "Herbivore"
end
def is_carnivore?
return diet_type == "Carnivore"
end
def is_cage_powered_down
if cage.is_powered_down?
errors.add(:cage_id, "Chosen cage is powered down. Please choose another cage!")
end
end
def is_cage_at_capacity
if cage.at_capacity?
errors.add(:cage_id, "Chosen cage is full. Please choose another cage!")
end
end
def cage_contains_diet_mismatch
if cage.has_carnivore == true and is_herbivore?
errors.add(:cage_id, "Chosen cage contains carnivores! This dinosaur will be eaten!")
else
if cage.has_herbivore == true and is_carnivore?
errors.add(:cage_id, "Chosen cage contains herbivores! This dinosaur will eat the others!")
end
end
end
end
spec/models/dinosaur_spec.rb
require 'rails_helper'
describe Dinosaur, type: :model do
it "is valid with valid attributes" do
dinosaur = Dinosaur.new(name:"Yellow", species:"Tyrranosaurus", diet_type:"Carnivore", cage_id: 7)
expect(dinosaur).to be_valid
end
it "is not valid without a name" do
dinosaur = Dinosaur.new(name: nil)
expect(dinosaur).to_not be_valid
end
it "is not valid without a max capacity" do
dinosaur = Dinosaur.new(species: nil)
expect(dinosaur).to_not be_valid
end
it "is not valid without a power status" do
dinosaur = Dinosaur.new(diet_type: nil)
expect(dinosaur).to_not be_valid
end
end
Upvotes: 0
Views: 809
Reputation: 164
It seems that when the execution of the program reaches the is_cage_at_capacity
method the cage is not set, actually the error has a clue: undefined method 'at_capacity?' for nil:NilClass
this means that you are trying to send the method at_capacity?
to a nil object.
So, I think some options to fix it will be to make sure a cage is associated with the Dinosaur when created or to add a guard clause to the is_cage_at_capacity
method, something like this:
def is_cage_at_capacity
if cage.nil?
errors.add(:cage_id, "There is not a cage associated with the dinosaur")
end
if cage.at_capacity?
errors.add(:cage_id, "Chosen cage is full. Please choose another cage!")
end
end
Upvotes: 0
Reputation: 1836
Why this happening?
When you call method valid?
all validations are triggered. At least, is_cage_at_capacity
calls dinosaur.cage.at_capacity?
. However, Dinosaur.new(diet_type: nil)
does not have any cage so the exception is raised.
How can you fix it?
The simplest way would be to add a cage in your tests:
cage = Cage.new
dinosaur = cage.build_dinosaur(params)
It could be quite repetetive to build all these objects every time, so consider using FactoryBot with associations.
When testing validations it is more precise to test an expected error instead of the whole object state. Check this.
For built-in validations (e.g. presence, uniqueness) better use shoulda-matchers.
Upvotes: 2