:authenticate method in Rspec - ruby-on-rails

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.

Related

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

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

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

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

Hartl's Ruby on Rails Section 7.3 Tutorial Sign-up Test Failure

After coding users_signup_test.rb and expecting a GREEN response, it is still failing for some reason. The tutorial states I should be getting a GREEN response at this phase, but I am not. This is what I receive:
1) Error:
UsersSignupTest#test_invalid_signup_information:
ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError
app/controllers/users_controller.rb:12:in `create'
test/integration/users_signup_test.rb:8:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/users_signup_test.rb:7:in `block in <class:UsersSignupTest>'
16 runs, 31 assertions, 0 failures, 1 errors, 0 skips
The only deviation I've had to do from the book that may affect this is one I had to make in the user.rb model and it is as follows:
validates( :email, presence: true, length: { maximum: 255}, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } )
as opposed to the book's:
validates( :email, presence: true, length: { maximum: 255}, format: { with: VALID_EMAIL_REGEX }, uniqueness: case_sensitive: false )
which would give me an error.
Here is my users_signup_test.rb file:
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, user: { name: "", email: "user#invalid", password: "foo", password_confirmation: "bar" }
end
assert_template 'users/new'
end
end
Let me know if any other code is needed to help diagnose this issue.
As an additional note, I also ran into issues that others have as well using sass-rails 5.0.1 and so I am using sass-rails 5.0.2.
Thank you everyone for your help.
Do you have well-defined the needed parameters (user_params function) when you create an user ? If you're following the tutorial you should end with something like this in your users_controller.rb:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# more logic
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
Because it looks like you're passing an user parameter that is not permitted by the controller.
Found the answer!
Sorry for the unnecessary post. Researched the error and found it's related to Strong Params. Looking closely at the book, I missed this line:
#user = User.new(user_params)
which changed from
#user = User.new(params[:user])
So I wasn't passing the parameters to the controller properly. Sorry everyone!

Resources