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.
Related
So, I have an unusual problem I don't really know how to fix. By nature, it's difficult to search for, so I'll try to describe as best I can and provide examples. When I test a single attribute in rspec for any model with a specific thing like numericality or uniqueness, I get an error saying that the other attributes cannot be blank instead of the one error rspec is expecting. I'll post my user model, as this is the simplest.
Model:
class User < ActiveRecord::Base
validates :name, :email, :password, presence: true
# validates :email, uniqueness: true
end
Factory:
FactoryGirl.define do
factory :user do
name { "Sir Artorias" }
email { "test#email.com" }
password { "AGreatPassword!" }
end
end
Spec:
require "spec_helper"
describe User do
describe "attributes" do
user = FactoryGirl.create(:user)
it { should validate_presence_of(:name) }
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should allow_value("e#e").for(:email) }
it { should_not allow_value("emailatexample.com").for(:email) }
it "stores all emails as downcase with white space truncated" do
user = create(:user, email: " Jo hn.Do e #exa mp le.c om ")
user.email.should == "john.doe#example.com"
end
it { should validate_presence_of(:password) }
it { should ensure_length_of(:password).is_at_least(8) }
end
describe "password is encrypted" do
user = FactoryGirl.create(:user)
subject { user.password }
it { should_not == "AGreatPassword!" }
end
end
Error example from using above spec:
5) User attributes should ensure password has a length of at least 8
Failure/Error: it { should ensure_length_of(:password).is_at_least(8) }
Expected errors to include "is too short (minimum is 8 characters)" when password is set to "xxxxxxx", got errors: ["name can't be blank (nil)", "email can't be blank (nil)"]
# ./spec/models/user_spec.rb:17:in `block (3 levels) in <top (required)>'
And this is what I'm referring to. I try to test a length restriction on password, and it errors out telling me that name cannot be nil and email cannot be nil. But I am using a factory above, and other tests which validate the presence of, say, name, don't fail.
Help would be very much appreciated. I can also post anything else you're curious about and think would help, i.e.; gemfile, rspec config, etc.
It's not telling you those errors are a problem, it's telling you it expected an entry saying the password was too short, but didn't. It then dumps the errors the array did contain so you can inspect.
Which makes sense, because in your model you are not validating length:
validates :name, :email, :password, presence: true
Try adding this to your User class:
validates_length_of :password, minimum: 8
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.
My User model contains the following:
validates :password_digest, :presence => true, :message => "The password has to be 6 or more characters long"
def password=(password)
self.password_digest = BCrypt::Password.create(password) if password.length >= 6
end
The issue is that the message in the validates isn't working. I get a Unknown validator: 'MessageValidator' error. I assumed the way the presence validation worked was that it would just check if the password_digest was nil, which it would be had the password had a length less than 6. I want a solution that is elegant, like what I attempted. I have solved this one way, but I would really appreciate an understanding as to why what I'm trying isn't working, and is there a way to make it work.
What I got to work was:
validate do |user|
user.errors['password'] = "can't be less than 6 characters" if user.password_digest.nil?
end
This is due to how the validates method works. It assumes that you're looking for the MessageValidator when you specify :message as a key in the hash passed to validates.
This can be solved by reconstructing the query as follows:
validates :password_digest, :presence => { :message => "The password has to be 6 or more characters long" }
I have a User model, and use an acts_as_authentic (from authlogic) on it. My User model have 3 validations on username and looks as following:
User < ActiveRecord::Base
acts_as_authentic
validates_presence_of :username
validates_length_of :username, :within => 4..40
validates_uniqueness_of :username
end
I'm writing a test to see my validations in action. Somehow, I get 2 errors instead of one when validating a uniqueness of a name. To see excess error, I do the following test:
describe User do
before(:each) do
#user = Factory.build(:user)
end
it "should have a username longer then 3 symbols" do
#user2 = Factory(:user)
#user.username = #user2.username
#user.save
puts #user.errors.inspect
end
end
I got 2 errors on username: #errors={"username"=>["has already been taken", "has already been taken"]}.
Another case of problem is when I set username to nil. Somehow I get four validation errors instead of three: #errors={"username"=>["is too short (minimum is 3 characters)", "should use only letters, numbers, spaces, and .-_# please.", "can't be blank", "is too short (minimum is 4 characters)"]}
I think authlogic is one that causes this strange behaviour. But I can't even imagine on how to solve that. Any ideas?
I think this is because authlogic has some build in validations and both them and your validations are run.
Google seems to give some answers to this topic. This one is for example for password field.
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