Rspec Spec - User Model validation on unique email account - spec failing - ruby-on-rails

Error message I am getting for my failing user spec is
User did not properly validate that :email is case-sensitively unique.
User Spec as follows:
it { expect(subject).to respond_to :emails }
it { expect(subject).to validate_uniqueness_of(:email).case_insensitive }
it 'has the correct format' do
expect(subject).to allow_value(Faker::Internet.email).for(:email)
expect(subject).to_not allow_value("'#{Faker::Internet.email}'").for(:email)
expect(subject).to allow_value("'some-thing.odd#example.com").for(:email)
end
it "requires a unique email" do
expect(subject).to validate_uniqueness_of(:email)
end
end`
Im not sure what I actually need to return for this to check case -sensitively uniqie, these are the first specs I am working on so also new to rspec but currently working through this book to understand rspec and testing better http://ruby-doc.com/docs/ProgrammingRuby/
User Model:
validates :email, presence: true, format: { with: ValidateEmail::REGEXP }
validates :alias_email, format: { with: ValidateEmail::REGEXP, allow_blank: true }
validates_uniqueness_of :email, case_sensitive: false

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

rails 5 validators not protecting against a missing attribute during Model create?

cannot seem to get my validators to work to ensure all attributes are present to allow a User to be created. Basic User with 2 attributes
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
tests to check that name and email are present when created. these #pass
RSpec.describe User, type: :model do
context 'validations' do
subject { FactoryGirl.build(:user) }
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_presence_of(:name) }
it "fails to create user unless both are present" do
expect { User.create(:name => 'jo bloggs1', :noemail => 'c#c.co')}.to raise_error(ActiveModel::UnknownAttributeError)
end
end
end
but if i try and create model with a missing attribute no error is raised
it "fails to create user unless both are present" do
expect { User.create(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
end
result
1) User validations fails to create user unless both are present
Failure/Error: expect { User.create(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
expected ActiveModel::MissingAttributeError but nothing was raised
# ./spec/models/user_spec.rb:12:in `block (3 levels) in <top (required)>'
fyi, FactoryGirl
FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
end
end
i have tried clever stuff like
class User < ApplicationRecord
# before_create :run_it
after_initialize :all_present?
validates :name, presence: true
validates :email, presence: true
private
def all_present?
if (#email.nil? || #name.nil?)
raise ActiveModel::MissingAttributeError.new()
end
end
end
but cannot seem to raise these manually...?
what am i doing wrong?
tx all
Ben
The problem is that there are 2 methods, create and create!. The first, create
The resulting object is returned whether the object was saved successfully to the database or not
Whereas with create!:
Raises a RecordInvalid error if validations fail, unlike Base#create
So, create fails silently and doesn't raise any exceptions, but you can still inspect the instance and see that it's a new record and has errors and such, and create! fails noisily, by raising the error you are expecting it to raise. In short, your test should be:
it "fails to create user unless both are present" do
expect { User.create!(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
end

How to get proper email uniqueness in RSpec

Rspec throws errors:
1) Client uniqueness validates uniqueness of email
Failure/Error: expect(subject).to validate_uniqueness_of :email
Client did not properly validate that :email is case-sensitively unique.
After taking the given Client, whose :email is
‹"jaleel_wehner#okonwiegand.name"›, and saving it as the existing
record, then making a new Client and setting its :email to a different
value, ‹"JALEEL_WEHNER#OKONWIEGAND.NAME"›, the matcher expected the
new Client to be valid, but it was invalid instead, producing these
validation errors:
* pesel: ["This pesel is already in database"]
* email: ["This email is already in database"]
In model I have implemented uniqueness and case-sensitive: false for email.
validates :email, presence: true,
uniqueness: { case_sensitive: false },
format: { with: VALID_EMAIL_REGEX }
I also have implemented method, that all email downcase before validation.
def downcase_email
self.email = email.downcase if email.present?
end
before_validation :downcase_email
Why matcher expected that new Client will be valid? It should be invalid.
subject { FactoryGirl.build(:client) }
it 'validates uniqueness of email' do
expect(subject).to validate_uniqueness_of :email
end
Client has a valid factory. I tried find good solution, but I haven't found anything that would solve my problem.
FactoryGirl.define do
factory :client do
pesel { Faker::Number.number(11) }
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
date_of_birth { Faker::Time.between('1970-01-01', '2000-12-31') }
email { Faker::Internet.email }
password { Faker::Internet.password }
type 'Client'
end
end
FactoryGirl has the following example for your issue within it's documentation
sequence :email do |n|
"person#{n}#example.com"
end
factory :invite do
invitee { generate(:email) }
end
Edit after your updates:
The issue is the matcher validate_uniqueness_of. You have to adjust case_sensitive for the matcher too. So it should be validate_uniqueness_of(:email).case_insensitive
Check if your factory has curved brackets wrapping the email attribute like such:
FactoryGirl.define do
factory :client do
email { Faker::Internet.email }
end
end

Testing conditional validation with controller inputs in Rails

I have custom validator for password that takes a updating_password field from the controller
attr_accessor :updating_password
validates :password, presence: true, if: :should_validate_password?
validates :password, length: { minimum: 6 }, if: :should_validate_password?
def should_validate_password?
updating_password || new_record?
end
I want to stub out the updating_password field in my User model RSpec test, something like
before(:each) do
#user_valid = FactoryGirl.create(:user)
end
it "validates for password when updating_password is true" do
old_password = #user_valid.password
subject { #user_valid }
allow(subject).to receive(:updating_password).and_return(true)
#user_valid.update(password: "short", password_confirmation: "short")
expect(#user_valid.password).to eql(old_password)
end
The password should not be updated in this case because it is too short but the test is failing. Any help would be appreciated
I'd recommend not stubbing out your model validations. Instead you might test what you're trying to do like this
# spec/models/user_spec.rb
describe User do
describe 'validations' do
context 'while updating password' do
let(:user){ FactoryGirl.create(:user, updating_password: true) }
it 'requires password to be at least 6 characters long' do
expect {user.update!(password: 'short')}.to raise_error(ActiveRecord:RecordInvalid)
end
it 'requires password to be present' do
expect {user.update!(password: nil))}.to raise_error(ActiveRecord:RecordInvalid)
end
end
end
end

:authenticate method in Rspec

I am currently following Michael Hartl's tutorial on RoR and am trying to get to grips more with Rspec testing. I am able to follow the majority of what is going on however I am a bit confused as to what and how the :authenticate method functions:
it { should respond_to(:authenticate) }
And how it relates to the following section of code:
describe "return value of authenticate" do
before { #user.save }
let(:found_user) { User.find_by_email(#user.email) }
describe "with valid password" do
it { should == found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
end
I understand the assignment a variable using let and the corresponding block, but I am struggling to understand what is happening when the authenticate method is called, what is it authenticating against in the initial line? Is it authenticating against the user.rb model that requires an email be present and that it matches a particular regex, or is it something else. I have included the user.rb code for clarity:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
validates :password_confirmation, presence: true
end
Any help much appreciated.
Without line numbers I might be misunderstanding which line you are referring to but here goes. The very first line of code you quote with the 'respond_to' method is merely asking the object whether it's interface handles whatever you are asking it to respond to. I think there is a bit missing - it should be: obj.should respond_to(:somemethod) It just checks that the object has a method with that name - so a fairly basic test.
Your question about what is going on when you call the authenticate method might be explained by the fact that you can't see the authenticate method in user.rb. In the older versions of the Hartl tutorial there is an authenticate method (I still have it on my machine :)
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
Looking at the way it is called in your code clearly it is implemented slightly differently off in a helper method or something... see the call to 'has_secure_password' in your model? That's a helper method where :authenticate lives.
Check the source code/documentation for more details.

Resources