Does anyone know how to check for invalid enum responses in rspec? I can check to ensure that the enum isn't nil with no errors but when I check to ensure that a bad enum value doesn't work, I get an error. These are the two specs:
it 'is not valid without question type' do
expect(build(:question, question_type: nil)).to have(1).errors_on(:question_type)
end
it 'is not valid with a bad question type' do
expect(build(:question, question_type: :telepathy)).to have(1).errors_on(:question_type)
end
This is what my model looks like:
class Question < ActiveRecord::Base
enum question_type: [ :multiple_choice ]
validates :question_type, presence: true
end
This is the error:
Failure/Error: expect(build(:question, question_type: :telepathy)).to have(1).errors_on(:question_type)
ArgumentError:
'telepathy' is not a valid question_type
This is my factory:
FactoryGirl.define do
factory :question, :class => 'Question' do
question_type :multiple_choice
end
end
Thanks for the help!
Your issue is that you need expect to execute the build as a proc to evaluate the result. Do this by changing the parentheses to curly brackets, as described in https://stackoverflow.com/a/21568225/1935918
it 'is not valid with a bad question type' do
expect { build(:question, question_type: :telepathy) }
.to raise_error(ArgumentError)
.with_message(/is not a valid question_type/)
end
Related
I have a model in this application that tracks role changes. The test I'm about to describe has been passing for the last couple years no problem and only starting failing as I upgraded the rails version from 5.1 to 5.2.
Here's an excerpt from the model:
class RoleChange < ApplicationRecord
acts_as_paranoid
belongs_to :actor, class_name: 'User', foreign_key: 'actor_id'
belongs_to :subject, class_name: 'User', foreign_key: 'subject_id'
validates :subject_id, presence: true
def subject
User.with_deleted.find(subject_id)
end
...
end
And the assertion that is failing in the spec is the following
require 'rails_helper'
RSpec.describe RoleChange, type: :model do
let(:organization) { create(:organization) }
let(:admin) { organization.users.admins.first }
let!(:user) { create(:employee_user, organization: organization) }
subject { create(:role_change, subject: user, actor: admin) }
describe 'associations' do
it { is_expected.to belong_to(:actor) }
it { is_expected.to belong_to(:subject) }
end
...
end
The second association assertion fails with the following error:
1) RoleChange associations is expected to belong to subject required:
Failure/Error: User.with_deleted.find(subject_id)
ActiveRecord::RecordNotFound:
Couldn't find User without an ID
# ./app/models/role_change.rb:28:in `subject'
This is very frustrating.
When I binding.pry inside of an it block, the subject appears to be valid? and persisted? with a subject_id as you'd expect.
It's only once I run the assertion, the subject_id magically becomes nil.
For additional context, my adventures in the rails console:
when I run the assertion, it fails and says that nil can’t be found:
Totally baffling. Can you help me finish this rails upgrade? It's my last failing test.
UPDATE:
deleting the subject getter method from RoleChange makes the test pass but then I lose the benefit of including deleted users from the query. So... doesn't actually seem like a solution.
I have a shoulda matcher in my achievement_spec.rb and I can't get it pass:
Spec:
- require 'rails_helper'
RSpec.describe Achievement, type: :model do
describe 'validations' do
it { should validate_presence_of(:title) }
it { should validate_uniqueness_of(:title).case_insensitive.scoped_to(:user_id).with_message("you
can't have two achievements with same title")}
it { should validate_presence_of(:user) }
it { should belong_to(:user) }
end
end
Model:
- class Achievement < ActiveRecord::Base belongs_to :user
validates :title, presence: true
validates :user, presence: true
validates :title, uniqueness: {
scope: :user_id,
message: "you can't have two achievements with the same title"
}
enum privacy: [ :public_access, :private_access, :friends_access ]
def description_html
Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(description)
end
end
Error:
- .F..
Failures:
1) Achievement validations should validate that :title is
case-sensitively unique within the scope of :user_id, producing a
custom validation error on failure
Failure/Error: it { should validate_uniqueness_of(:title).scoped_to(:user_id).with_message("you
can't have two achievements with same title") }
Achievement did not properly validate that :title is case-sensitively
unique within the scope of :user_id, producing a custom validation error
on failure.
After taking the given Achievement, setting its :title to ‹"an
arbitrary value"›, and saving it as the existing record, then making a
new Achievement and setting its :title to ‹"an arbitrary value"› as
well and its :user_id to a different value, ‹nil›, the matcher
expected the new Achievement to be invalid and to produce the
validation error "you can't have two achievements with same title" on
:title. The record was indeed invalid, but it produced these
validation errors instead:
* user: ["can't be blank"]
* title: ["you can't have two achievements with the same title"]
# ./spec/models/achievement_spec.rb:29:in `block (3 levels) in <top (required)>'
Finished in 0.16555 seconds (files took 3.13 seconds to load) 4
examples, 1 failure
Failed examples:
rspec ./spec/models/achievement_spec.rb:29 # Achievement validations
should validate that :title is case-sensitively unique within the
scope of :user_id, producing a custom validation error on failure
[Finished in 4.1s with exit code 1] [cmd: ['rspec', '-I
/home/mayur/Downloads/MyWork/ruby/i-rock/spec/models',
'/home/mayur/Downloads/MyWork/ruby/i-rock/spec/models/achievement_spec.rb']]
[dir: /home/mayur/Downloads/MyWork/ruby/i-rock] [path:
/usr/local/rvm/gems/ruby-2.2.4/bin:/usr/local/rvm/gems/ruby-2.2.4#global/bin:/usr/local/rvm/rubies/ruby-2.2.4/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/usr/local/rvm/bin:/home/mayur/.local/bin:/home/mayur/bin]
How can I get rid of above error?
I tried below solution but getting same error:
Change in model:
validates :title, uniqueness: {
case_sensitive: false,
scope: :user_id,
message: "you can't have two achievements with the same title"
}
Change in spec:
it { should validate_uniqueness_of(:title).case_insensitive.scoped_to(:user_id).with_message("you can't have two achievements with same title") }
Error Again:
.F..
Failures:
1) Achievement validations should validate that :title is case-insensitively unique within the scope of :user_id, producing a custom validation error on failure
Failure/Error: it { should validate_uniqueness_of(:title).case_insensitive.scoped_to(:user_id).with_message("you can't have two achievements with same title") }
Achievement did not properly validate that :title is case-insensitively
unique within the scope of :user_id, producing a custom validation error
on failure.
After taking the given Achievement, setting its :title to ‹"an
arbitrary value"›, and saving it as the existing record, then making a
new Achievement and setting its :title to ‹"an arbitrary value"› as
well and its :user_id to a different value, ‹nil›, the matcher
expected the new Achievement to be invalid and to produce the
validation error "you can't have two achievements with same title" on
:title. The record was indeed invalid, but it produced these
validation errors instead:
* user: ["can't be blank"]
* title: ["you can't have two achievements with the same title"]
# ./spec/models/achievement_spec.rb:29:in `block (3 levels) in <top (required)>'
Finished in 0.13566 seconds (files took 3.14 seconds to load)
4 examples, 1 failure
Failed examples:
rspec ./spec/models/achievement_spec.rb:29 # Achievement validations should validate that :title is case-insensitively unique within the scope of :user_id, producing a custom validation error on failure
The failure is occurring due to the error message defined in your spec not matching what's in your model. You're missing the word the in your spec:
Spec
... .with_message("you can't have two achievements with same title")}
Model
... message: "you can't have two achievements with the same title"
Fixing what's defined in your spec in .with_message to match what's defined in your model within the uniqueness validation message on title should resolve the failure.
You need to build a factory and ensure there is a user before running these validations:
describe Achievment do
context 'validations' do
before { FactoryGirl.build(:achievement) }
it do
should validate_uniqueness_of(:title).
scoped_to(:user_id).
case_insensitive
end
end
end
your factory then:
FactoryGirl.define do
factory :achievement
title 'some-string'
user
end
end
I'm having troubles testing an ActiveRecord inclusion validation in Rails with Factory Girl and Rspec. The inclusion validation always fails. Here is my code:
class FruitType
has_many :fruits
end
class Fruit
belongs_to :fruit_type
validates :fruit_type_id, numericality: { only_integer: true }
validates :fruit_type_id, inclusion: { in: FruitType.pluck(:id), message: "is invalid" }
end
FactoryGirl.define do
factory :fruit_type_apple, class: FruitType do
name "Apple"
end
end
FactoryGirl.define do
factory :valid_fruit, class: Fruit do
name "Red Apple"
association :fruit_type, factory: :fruit_type_apple
end
end
Rspec test is:
it "should have valid factory" do
f = FactoryGirl.build( :valid_fruit )
puts f.fruit_type_id
puts "\n#{FruitType.all.pluck(:id)}"
expect(f).to be_valid
end
Result is:
1
[1]
F..........
Failures:
1) Fruit when validated should have valid factory
Failure/Error: expect(f).to be_valid
expected # to be valid, but got errors: Fruit type is invalid
As you can see, I've printed out the Fruit Type id list in the test, which includes only 1. And I've printed out the value of fruit_type_id for the fruit, which is 1. Yet, the inclusion validation still fails.
If I do the same thing in the rails console just by creating fruits and types manually, the validation works fine, it's just when I run the test I'm seeing this behavior. Any ideas? I must be missing something about Factory Girl here.
You shouldn't validate the fruit_type_id attribute, rather use presence validation
validates :fruit_type, presence: true
REVISED: the error here was that I was storing several different models in the same hash. This was an internal way with how I was constructing the array. Anyway, I apologize for the error here. There was no way one could have answered the question with how I asked it.
So I have an RSpec before(:each) block in a controller spec. My example model has a status field and the following validation:
class Model < ActiveRecord::Base
STATI = [ "vacant", "deleted", "deactivated"]
...
validates :status, :inclusion => { :in => STATI }
...
end
And in my spec, I have the following code.
describe Controller do
...
describe "some methods" do
before(:all) do
#models = []
10.times { #models << Factory(:model) }
end
before(:each) do
#models.each { |m| m.update_attributes(:status => "vacant") }
end
...
end
end
When I run the spec, all the other describe blocks run fine. It pulls an an error to the effect of:
ActiveRecord::RecordInvalid:
Validation failed: Status is not included in the list
and points to the line where it says m.update_attributes(:status => "vacant").
Thank you for any help.
I would try the following in your model definition:
class Model < ActiveRecord::Base
STATI = %w[vacant deleted deactivated]
...
validates :status, :inclusion => STATI
...
end
The %w is preferred syntax for creating an array of strings, and allows the removal of " and , from your array definition.
You do not need the :in => for an inclusion validation.
I'm trying to capture the value that's throwing a uniqueness error (or for that matter, any other type of built-in validation) to display it in the :message option. Here's what I tried (didn't work)
# inside the model
validate_uniqueness_of :name, :message => "#{name} has already been taken" # also tried using #{:name}
I could use a custom validation, but this beats the point of using something that's already integrated into AR. Any thoughts? thanks.
Try this interpolation technique:
validate_uniqueness_of :name, :message => "%{value} has already been taken"
The RailsGuide for Active Record Validations and Callbacks shows an example where %{value} is interpolated in a custom error message:
:message => "%{value} is not a valid size"
I looked at the validates_each documentation and can see the validate block is passed three properties: |record, attr, value|. All three can be accessed with %{model}, %{attribute} and %{value}.
While this is limited, since it only gives you access to three properties, thankfully that is all you need.
Try self.name
validates_uniqueness_of :name, :message => "#{self.name} has already been taken" # also tried using #{:name}
Also validate_uniqueness_of is wrong it should be validates_uniqueness_of
If this not works use validate method and comment line validates_uniqueness_of
def validate
name= User.find_by_name(self.name) #Assuming User is your Model Name
unless name.blank?
self.errors.add :base, "#{self.name} has already been taken"
end
end