Shoulda rspec matchers :on => :create - ruby-on-rails

I am using some of the Shoulda rspec matchers to the test my model, one of them being:
describe Issue do
it { should_not allow_value("test").for(:priority) }
end
My problem with this is that my validation in my model looks like this:
validates_format_of :priority, :with => /^(Low|Normal|High|Urgent)$/, :on => :update
So when running this test I get:
1) 'Issue should not allow priority to be set to "test"' FAILED
Expected errors when priority is set to "test", got errors: category is invalid (nil)title can't be blank (nil)profile_id can't be blank (nil)
The validation isn't being triggered because it only runs on an update, how can I use these shoulda matchers on an update vs. a create?

I think shoulda should handle this better. I ran into this because I only want to run my uniqueness validation check for my User model when creating new users. It's a waste of a database query doing it on update, since I don't allow usernames to be changed:
validates :username, :uniqueness => { :case_sensitive => false, :on => :create },
Fortunately you can get around this by explicitly defining the "subject":
describe "validation of username" do
subject { User.new }
it { should validate_uniqueness_of(:username) }
end
This way it's only testing on a new instance. For your case, you can probably just change the subject to be something saved in the database already, with all the necessary fields set.

Related

rails unit test fails although valid data

I'm new to rails testing and have written a simple unit test to check my validations. I want to check if my sample data is valid and check the name and email field.
bill_test.rb
test "sample data is valid" do
assert Bill.new(name: bills(:one).name, email: bills(:one).email).valid?, 'Sample data is not valid.'
end
My model:
validates_presence_of :name
validates :name, :length => {:maximum => 30 }
validates_presence_of :email
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :if => :email?
after running "rake test:units"
1) Failure:
BillTest#test_sample_data_is_valid [/Users/martinbraun/Documents/Projekte/pay-ma-bill/test/models/bill_test.rb:10]:
Sample data is not valid.
My fixture:
one:
name: Hans
email: hans#gmail.com
I also get the failure when removing the validations so I guess the mistake lies in my actual assert. But I cannot see any error there.
Any ideas?
visit this page, it may be helpful for you.

Testing password length validation with RSpec

I'm writing some unit tests to ensure a User model cannot have a password < 8 characters long.
I started with a User model:
class User < ActiveRecord::Base
...
validates :password, :length =>{
:minimum => 90,
:too_short => "password is too short, must be at least %{count} characters"
}, :on => :create
end
And a user_spec.rb test:
describe User do
subject { FactoryGirl.build :user }
its(:password) { should have_at_least(8).items }
end
However I realised that this doesn't actually test my validation, it just tests that my factory had a password >= 8 characters.
Is there a nice way to do this other than testing the valid? method for 0-7 character passwords?
My theory is that if I only test for 7 characters and someone accidentally hard codes that 4 characters passwords are OK this would pass validation but isn't really what was intended.
There could be some code else where that depends on a password being more than 8 characters (not likely but in other situations could be true) and so allowing a password of 4 is incorrect.
In this case the person who changed the password validation in the model won't know that that they've done anything wrong.
I'd just like to know how to properly test situations like this nicely with TDD.
Using the ensure_length_of matcher in thoughtbot's shoulda matchers, you can do this:
describe User do
it { should validate_length_of(:password).is_at_least(8)
.with_message(/password is too short/) }
end
See also: How can I test :inclusion validation in Rails using RSpec
I don't know if this answers your question but I think you are safe with something like this:
class User < ActiveRecord::Base
validates :password, :length => {:minimum => 8 }
end
describe User do
it "validates password length" do
FactoryGirl.build(:user, password: "1234567").should_not be_valid
FactoryGirl.build(:user, password: "12345678").should be_valid
end
end
The reason is that for this test to let through a 4 character password somebody would have to have set a validation rule that says 4 characters is ok, 7 isn't ok, but 8 is ok. Not something that is likely to happen by accident.

Validation Error in RSpec before(:each) block has weird behavior

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.

Allow blank on should validate_uniqueness_of

Using shoulda, any ideas how to allow blank when having this test:
should validate_uniqueness_of(:email)
Thanks.
I have figured it out!! The validate_uniqueness_of relies on the value for the attribute you are currently testing on the first record in the database. So, if the first record in the database happens to be blank or nul for that attribute your test will always give an error like this: "Failure/Error: it { should validate_uniqueness_of(:customer_number).case_insensitive.scoped_to(:user_id) } Expected errors to include "has already been taken" when customer_number is set to nil, got no errors".
So how do we fix this? Make sure you delete all existing records and that the first record contains a filled in value for the attribute.
model:
validates_uniqueness_of :customer_number, :scope => :user_id, :case_sensitive => false, :allow_blank => true, :allow_nil => true
model spec:
describe 'Validation' do
it { should allow_value('').for(:customer_number) }
describe 'When a user exists with the same customer_number and user' do
before(:each) do
Customer.destroy_all
# Saving a single customer to validate uniqueness.
#existing = Factory(:customer, :customer_number => 'test') # If you leave customer_number blank here the matcher will check if it can save a new record with a blank customer_number which is possible since we allow blanks. So make sure your first record has a filled in value!!
end
subject do
Factory.build(:customer)
end
it { should validate_uniqueness_of(:customer_number).case_insensitive.scoped_to(:user_id) }
end
end
Hope this cleared up some people's issues :)
should allow_value(" ").for(:email)
should allow_value(nil).for(:email)
maybe
should validate_uniqueness_of(:email, :allow_blank => true)

How to test custom messages thrown back by a models validation in Rails

I have this validation in my user model.
validates_uniqueness_of :email, :case_sensitive => false,
:message => "Some funky message that ive cleverly written"
In my tests I want to ensure that when a user enters a dupe email address that my message definately gets shown but without having to duplicate the error string from above in my test. I dont like that because im sure the message will change as i start thinking about copy. Does rails store these error messages - something which i can call in my tests?
Ive done a general test of
assert #error_messages[:taken] , user.errors.on(:email)
but that would pass on any of the other email related errors ive set validations up to catch i.e. incorrect formating, blank etc.
I made a quick test, and it looks like the error messages are sorted in the order you wrote your validation statements in your model class (top-down).
That means, you can find the error message for the first validation on an attribute at the first place in the errors array:
user.errors.on(:email)[0]
So, if your user model class contains something like this:
validates_presence_of :email
validates_uniqueness_of :email, :case_sensitive => false, :message => "Some funky message that ive cleverly written"
validates_length_of :email
...you'll find your 'funky message' at user.errors.on(:email)[1], but only if at least validates_presence_of triggers an error, too.
Concerning your specific problem:
The only way I could think of to not repeat your error message in the test, is to define a constant in your user model and use this instead of directly typing a message for that validation:
EMAIL_UNIQUENESS_ERROR_MESSAGE = "Some funky message that ive cleverly written"
...
validates_uniqueness_of :email, :case_sensitive => false, :message => EMAIL_UNIQUENESS_ERROR_MESSAGE
In your test, you could use this constant, too:
assert_equal User::EMAIL_UNIQUENESS_ERROR_MESSAGE, user.errors.on(:email)[1]
In rspec,
it "should validate uniqueness of email" do
existing_user = User.create!(:email => email)
new_user = User.create!(:email => existing_user.email)
new_user.should_not be_valid
new_user.errors.on(:email).should include("Some funky message that ive cleverly written")
end

Resources