Reputation: 145
I'm new to testing Rails so this is probably a very small thing but I can't figure out what's wrong. So I have some models I'd like to test. Right now the tests are simple; testing the presence of attributes and saving if all validations are met.
One of my models Profile
belongs_to
my Users
model and passes all these tests spec/models/profiles_spec.rb
:
require 'rails_helper'
RSpec.describe Profile, type: :model do
context 'validation tests' do
it 'ensures user_id presence' do
profile = Profile.new(platform: 0, region: 0, tag: 'GamerTag', sr: 1600).save
expect(profile).to eq(false)
end
it 'ensures platform presence' do
profile = Profile.new(user_id: 1, region: 0, tag: 'GamerTag', sr: 1600).save
expect(profile).to eq(false)
end
it 'ensures region presence' do
profile = Profile.new(user_id: 1, platform: 0, tag: 'GamerTag', sr: 1600).save
expect(profile).to eq(false)
end
it 'ensures tag presence' do
profile = Profile.new(user_id: 1, platform: 0, region: 0, sr: 1600).save
expect(profile).to eq(false)
end
it 'ensures sr presence' do
profile = Profile.new(user_id: 1, platform: 0, region: 0, tag: 'GamerTag').save
expect(profile).to eq(false)
end
it 'should save successfully' do
profile = Profile.new(user_id: 1, platform: 0, region: 0, tag: 'GamerTag', sr: 1600).save
expect(profile).to eq(true)
end
end
end
app/models/profile.rb
:
class Profile < ApplicationRecord
validates :platform, presence: true
validates :region, presence: true
validates :tag, presence: true
validates :sr, presence:true
belongs_to :user
enum platform: [:pc, :xbl, :psn]
enum region: [:us, :eu]
end
But then there are my other models, which "pass" all the attribute presence validation tests, which theres something wrong there because they still pass when I comment out their attribute validations and fail the 'should save successfully'
test.
The most confusing part? When I run the rails console and manually test it returns the expected value (true), like with my Student
model which belongs_to :profile
.
So I really have no idea what's going on here. Any ideas please throw them out. If you all need any more information, let me know.
Upvotes: 1
Views: 3602
Reputation: 230521
Indeed, it's an error of missing related records. Let's take coach spec, for example:
it 'should save successfully' do
coach = Coach.new(profile_id: 1, roles: ['tank']).save
expect(coach).to eq(true)
end
Here (in my experiments) there's no profile with id=1. In fact, there are no profiles at all. So this spec fails, as expected.
Buuuut, by the time we get to the profile spec:
it 'should save successfully' do
profile = Profile.new(user_id: 1, platform: 0, region: 0, tag: 'GamerTag', sr: 1600)
expect(profile).to eq(true)
end
user with id=1 does exist (likely because user spec was run before this one and successfully created a user record).
Lessons to learn:
Upvotes: 3
Reputation: 15848
First of all, you are writing tests in a really inefficient way. If you wan't to test validations, then you don't need to test the save
method return value but the value of the valid?
method AND the errors
hash.
RSpec.describe Profile, type: :model do
context 'validation tests' do
it 'ensures user_id presence' do
profile = Profile.new(platform: 0, region: 0, tag: 'GamerTag', sr: 1600, user_id: nil) #you should be explicit with the user_id value being nil, tests should be explicit, it may seem unnecesary but it makes them easier to read
expect(profile).to be_invalid #or expect(profile).not_to be_valid
expect(profile.errors[:user_id]).to be_present #you could test the actual message too, not just the presence on any error
end
end
end
That test actually tests only validations and also ensures that there's an error on the user_id
field.
With your actual test you cannot know what is actually preventing the object to be saved. It could be anything: another validation, a before_save callback returning false, an invalid value when inserting into the database, anything. It's also slower since it has to actually write the record on the database, testing valid?
is done on memory which is a lot faster.
Id' recommend you to read about FactoryBot so you don't have to repeat Profile.new....
on each test.
If you still want to test the return value of save
on the last test, you have to know WHY it's not being saved, you con use save!
which raises an exception instead of returning false to debug your code, and you can also inspect profile.errors.full_messages
to see if there are any errors that you didn't consider when setting up the test.
Upvotes: 1