rails devise validation password_confirmation not working - ruby-on-rails

I am having trouble with devise validation. This is my model class (Note that I have enabled validatable):
class User < ApplicationRecord
devise :database_authenticatable, :registerable, :recoverable,
:rememberable, :trackable, :validatable
end
And I try to validate the password confirmation using:
describe User do
before { #user = FactoryGirl.build(:user) }
subject { #user }
it { should validate_confirmation_of(:password) }
end
My factory:
FactoryGirl.define do
factory :user do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
But i got this error:
1) User should require password_confirmation to match password
Failure/Error: it { should validate_confirmation_of(:password) }
Expected errors to include "doesn't match Password" when password is set to "different value",
got no errors
# ./spec/models/user_spec.rb:18:in `block (2 levels) in <top (required)>'
Which means the devise validator is not triggered.
Now I add a custom validator:
validate :password_must_match
def password_must_match
errors.add(:password, "doesn't match confirmation") if password != password_confirmation
end
And got this error:
Failures:
1) User should require password_confirmation to match password
Failure/Error: it { should validate_confirmation_of(:password) }
Expected errors to include "doesn't match Password" when password is set to "different value",
got errors:
* "doesn't match Password" (attribute: password_confirmation, value: "some value")
* "doesn't match confirmation" (attribute: password, value: "different value")
# ./spec/models/user_spec.rb:18:in `block (2 levels) in <top (required)>'
As you can see, we now have 2 validation errors, "doesn't match Password" is from devise's validatable, and "doesn't match confirmation" is from my own custom validator.
I also tried using a custom test instead of it { should validate_confirmation_of(:password) }
describe "#confirmation_of_password" do
it "should fail when password does not match" do
expect { User.create!(email: "xxx#xxx.com", password: "123456", password_confirmation: "1234567") }.to raise_error
end
end
And it works. But I dont wanna reinvent the wheel from shoulda

In your first test You expect that it will validate confirmation of password but you pass in the same password so it never gets a chance to fail. SO you have 3 options. 1 use your first test but change the confirmation to not be the same(by passing in the params via factory girl). 2 you can expect to raise(if you do this you should specify which error will raise). Or you can also use Factory Girl's build method and expect the user to not be valid.

Related

RSpec for email format validation failed

I've got user devise model with validations
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates :first_name, presence: true, length: { maximum: 100 }
validates :last_name, presence: true, length: { maximum: 100 }
validates :email, presence: true, uniqueness: true, format: { with: /\A.+#.+\..+\z/ }
end
and RSpec file to test email validation
describe 'email field' do
subject { User.new(first_name: 'jan', last_name: 'kowalski', email: 'jan#foo.com').valid? }
context 'when email has wrong format' do
let(:email) { 'jan#foo' }
it 'complains for invalid format' do
is_expected.to eq false
end
let(:email) { 'jan' }
it 'complains for invalid format' do
is_expected.to eq false
end
end
context 'when email has correct format' do
it 'accepts valid format' do
is_expected.to eq true
end
end
end
I want to test the validations for correct email address format which is in user model. Every test passed well except the last where I have an error expected: true got: false. Did I miss something in a spec file? or maybe I have wrong declaration in user model? Any helps are welcomed.
you have some mistakes in your spec file, the two let of email are not execution in anyway.
if you want that behavior you need to replace the email option in your subject by a variable, and your second it you need to wrap it in a context and put inside you let(:email), this is the way that rspec is going to replace the value of your subject in each it.
Here an example but using a password variable, also your two first test are passing, because your are missing the password that way your expec is false, but they are not test the test your are describing.
describe 'password field' do
subject { Usuario.new(nombre: 'jan', username: 'kowalski', password: password).valid? }
context 'when password has wrong format' do
let(:password) { nil }
it 'complains for invalid format' do
is_expected.to eq false
end
end
context 'when password size is incorrect' do
let(:password) { 'jan' }
it 'complains for invalid format' do
is_expected.to eq false
end
end
context 'when password has correct format' do
let(:password) { '1qaz2wsx' }
it 'accepts valid format' do
is_expected.to eq true
end
end
end

Shoulda-matchers test with FactoryGirl, password_confirmation not set?

I am testing my account model with shoulda-matchers using a entity created with FactoryGirl.
The code of both files look like this:
TEST FILE
require 'spec_helper'
describe Account do
before { #account = FactoryGirl.build(:account) }
subject { #account }
it { should validate_presence_of(:email) }
it { should validate_presence_of(:password) }
it { should validate_presence_of(:password_confirmation).
with_message(#account.password_confirmation+' '+#account.password) }
it { should allow_value('example#domain.com').for(:email) }
it { should be_valid }
end
FACTORY
FactoryGirl.define do
factory :account do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
My error is the following:
1) Account should require password_confirmation to be set
Failure/Error: it { should validate_presence_of(:password_confirmation).
Expected errors to include "12345678 12345678" when password_confirmation is set to nil, got errors: ["password_confirmation can't be blank (nil)"]
# ./spec/models/account_spec.rb:9:in `block (2 levels) in <top (required)>'
rspec ./spec/models/account_spec.rb:9 # Account should require password_confirmation to be set
I am using devise which should check for password confirmation. I know its probably because of something stupid, but I really can't figure out what's wrong with it.
Devise, doesn't validate the presence of password_confirmation it just validates the confirmation of password see Validatable line 34: https://github.com/plataformatec/devise/blob/v3.5.3/lib/devise/models/validatable.rb#L34
validates_confirmation_of :password, if: :password_required?
EDIT: You could also see that there are no validators on password_confirmation running: User.validators_on(:password_confirmation)

Rails - Testing User Model with Rspec and Devise

I am trying to Test the User Model Spec for User Creation
factories/users.rb
FactoryGirl.define do
factory :user do
first_name {Faker::Name.first_name}
last_name {Faker::Name.last_name}
email {Faker::Internet.email}
username {Faker::Internet.user_name}
password {Faker::Internet.password}
end
end
specs/models/user_spec.rb
require 'rails_helper'
describe User, :type => :model do
context "valid Factory" do
it "has a valid factory" do
expect(build(:user)).to be_valid
end
end
context "validations" do
before { create(:user) }
context "presence" do
it { should validate_presence_of :first_name }
it { should validate_presence_of :last_name }
it { should validate_presence_of :email }
it { should validate_presence_of :encrypted_password }
end
context "uniqueness" do
it { should validate_uniqueness_of :email }
it { should validate_uniqueness_of :username }
end
end
end
I am using Devise for the USer creation. But i am ending up with the following Test Failure
User
valid Factory
has a valid factory
validations
presence
should require first_name to be set
should require last_name to be set
should require email to be set
should require encrypted_password to be set (FAILED - 1)
uniqueness
should require case sensitive unique value for email
should require case sensitive unique value for username
Failures:
1) User validations presence should require encrypted_password to be set
Failure/Error: it { should validate_presence_of :encrypted_password }
Expected errors to include "can't be blank" when encrypted_password is set to nil,
got no errors
# ./spec/models/user_spec.rb:18:in `block (4 levels) in <top (required)>'
Finished in 0.98827 seconds (files took 6.61 seconds to load)
7 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:18 # User validations presence should require encrypted_password to be set
Am assuming that the encrypted_password will be auto generated by Devise on trying to create the user.
Devise does not actually validate the encrypted password since it is created dynamically after validation if the password is "dirty" (changed).
You don't actually need to test the encrypted password in that way since it is an implementation detail. You can test that Database authenticable is working properly by doing something like:
it 'is database authenticable' do
user = User.create(
email: 'test#example.com',
password: 'password123',
password_confirmation: 'password123'
)
expect(user.valid_password?('password123')).to be_truthy
end
But the actual value of the test is pretty low. Instead with Devise you you may want to focus on some end-to-end tests (feature specs) where you test the user path of signing up and then logging in.
I would suggest you only test those validations you added, you do not need to test validations added by devise since they are already been tested.

Rspec be_valid fails on a valid test

I'm learning TDD with Rails 4 and rspec. I've made some test cases for my user model to check the password lengths. I have two tests so far that checks whether a user input a password that was too short and one where the password is between 6 - 10 characters.
So far, the "password is too short" test passes:
it "validation says password too short if password is less than 6 characters" do
short_password = User.create(email: "tester#gmail.com", password: "12345")
expect(short_password).not_to be_valid
end
However, on the test where I do have a valid password, it fails:
it "validation allows passwords larger than 6 and less than 10" do
good_password = User.create(email: "tester2#gmail.com", password: "blahblah")
expect(good_password).to be_valid
end
And I get this error:
Failure/Error: expect(good_password).to be_valid
expected #<User id: 1, email: "tester2#gmail.com",
created_at: "2014-06-21 02:43:42", updated_at: "2014-06-21 02:43:42",
password_digest: nil, password: nil, password_hash: "$2a$10$7u0xdDEcc6KJcAi32LBW7uzV9n7xYbfOhZWdcOnU5Cdm...",
password_salt: "$2a$10$7u0xdDEcc6KJcAi32LBW7u"> to be valid,
but got errors: Password can't be blank, Password is too short (minimum is 6 characters)
# ./spec/models/user_spec.rb:12:in `block (3 levels) in <top (required)>'
Here's my model code:
class User < ActiveRecord::Base
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :pets, :allow_destroy => true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: true
validates :password, presence: true, :length => 6..10, :confirmation => true
#callbacks
before_save :encrypt_password
after_save :clear_password
#method to authenticate the user and password
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
#method to encrypt password
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
#clears password
def clear_password
self.password = nil
end
end
I'm confused on why the password is nil when I create the test object.
You have a password presence requirement on your model, but then you have an after_save hook that nilifies the password and puts the record into an invalid state. The first test passes because your records are always being put into an invalid state by the after_save hook. You need to rethink how you're handling password storage; once you resolve that, here are some code samples to help give you some ways to test this:
# Set up a :user factory in spec/factories.rb; it should look something like:
FactoryBot.define do
factory :user do
sequence(:email) { |n| "tester+#{n}#gmail.com" }
password { SecureRandom.hex(6) }
end
end
# In your spec:
let(:user) { create :user, password: password }
context 'password' do
context 'length < 6' do
let(:password) { '12345' }
it { expect(user).not_to be_valid }
it { user.errors.message[:password]).to include('something') }
end
context 'length >= 6' do
context 'length < 10' do
let(:password) { 'blahblah' }
it { expect(user).to be_valid }
end
context 'length >= 10' do
let(:password) { 'blahblahblah' }
it { expect(user).not_to be_valid }
end
end
end
You can also use shoulda matchers:
it { should_not allow_value('12345').for(:password) }
it { should allow_value('12345blah').for(:password) }
The most likely problem is the password field is not mass assignable. That is why password is nil in the output message.
Try this instead:
it "validation allows passwords larger than 6 and less than 10" do
good_password = User.create(email: "tester2#gmail.com")
good_password.password = "blahblah"
expect(good_password).to be_valid
end
Note that your first test is passing accidentally - it has the same problem as the second test (password isn't being assigned). This means you aren't actually testing that the password is rejected when less than 6 characters atm.
See this article on mass assignment for more details.
EDIT: Leo Correa's comment may suggest this may not be the case for you. Posting your model code would help...

Rails Rspec Model Spec User :email ActiveRecord::RecordInvalid

Trying to figure out why my rspec test is failing. Most notable is the Failure message that seems contradictory. Stating I have an ActiveRecord::RecordInvalid error and that is exactly what I'm asserting should happen.
Here is my user.rb
...
validates_presence_of :email
...
Here is my users_spec.rb
...
it "is invalid without email" do
Factory(:user, email: nil).should raise_error(ActiveRecord::RecordInvalid)
end
...
here is the output:
Failures:
1) User a user (in general) is invalid without email
Failure/Error: Factory(:user, email: nil).should raise_error(ActiveRecord::RecordInvalid)
ActiveRecord::RecordInvalid:
Validation failed: Email is invalid, Email can't be blank
# ./spec/models/user_spec.rb:34:in `block (3 levels) in <top (required)>'
Originally I was testing it this way but it kept failing, so I decided to specify on what error I was expecting.
it "is invalid without email" do
Factory(:user, email: nil).should_not be_valid
end
The reason your code isn't working is that you're trying to create an invalid model before actually testing it for validity. What you want to do is to create a valid model, change something and check that it is invalid, like this:
it "is invalid without email" do
user = Factory(:user)
user.email = nil
user.should_not be_valid
end
I personally like to define my model in a before block, set is as the subject and then change attributes in each spec and check for validity, like this:
before do
#user = FactoryGirl.create(:user)
end
subject { #user }
it "is invalid without email" do
subject.email = nil
should_not be_valid
end
For the record, if you wanted to test that the record creation raised an error (which is definitely not the advisable way to do this), you could do it by wrapping the Factory call in a lambda, like this:
lambda {
Factory(:user, :email => nil)
}.should raise_error(ActiveRecord::RecordInvalid)

Resources