app works, but in rSpec test fails - ruby-on-rails

In User model I have following methods:
def generate_and_set_new_password!
password = random_pronouncable_token
self.password_hash = encrypt(password)
self.password_reset_sent_at = Time.now
if self.save!
password
else
nil
end
end
private
def encrypt(string)
# Some code that encrypts string
# ...
end
def random_pronouncable_token(size = 4)
# Some code that generates new password
# ...
end
rSpec test:
describe "New password generation" do
it "should generate new password" do
user = #user
old_pwd_hash = user.password_hash
pwd = user.generate_and_set_new_password!
pwd.should_not be_nil
user.password_hash.should_not == old_pwd_hash
end
end
This test gives me such output:
1) User password encryption New password generation should generate new password
Failure/Error: user.password_hash.should_not == old_pwd_hash
expected not: == "966abfff742d73c71fc87a1928b90aec"
got: "966abfff742d73c71fc87a1928b90aec"
' generate_and_set_new_password! ' method correctly works in app, here is example from console:
ruby-1.8.7-p249 :014 > user.password_hash
=> "ef10fb44dfceeaca05ca78030c1d8cb5"
ruby-1.8.7-p249 :015 > user.generate_and_set_new_password!
=> "quispopeho"
ruby-1.8.7-p249 :016 > user.password_hash
=> "a0b7605d5d6ca2152f7e019b4896fef4"
ruby-1.8.7-p249 :017 >
As we can see, it gives new password_hash for user, so it works correctly.
I decided that this is some bug in rSpec. I've made such experiment:
Here is some changes in test's code:
describe "New password generation" do
it "should generate new password" do
user = #user
old_pwd_hash = user.password_hash
p "old_password = ", user.password
p "old_pwd_hash = ", old_pwd_hash
pwd = user.generate_and_set_new_password!
p "received new password = #{pwd}"
p "SHOULD BE a new_pwd_hash, but it isn't! ", user.password_hash
p "SHOULD BE a new_password, but it isn't! ", user.password
pwd.should_not be_nil
# There I am tring to set this new password explicitly :)
user.password = pwd
user.save
p "NOW it is other password hash = ", user.password_hash
user.password_hash.should_not == old_pwd_hash
end
end
Now test is passed!
And the debugging output is the following:
"old_password = foobar"
"old_pwd_hash = c251e8a9bcc4fba1199181b1f2c1430c"
"received new password = kongibyty"
"SHOULD BE a new_pwd_hash, but it isn't! c251e8a9bcc4fba1199181b1f2c1430c"
"SHOULD BE a new_password, but it isn't! foobar"
"NOW it is other password hash = e2b53133b796bcb212a0c1fa1d2a1cfa"
As I can see from this debugging output, user object get it's new password_hash ONLY after it was EXPLICITLY set up in test's code. Seems like 'generate_and_set_new_password!' method can't change object that was created in test environment.
Why I am getting such weird rSpec's behaviour?
Any suggestions will be useful.
Some additional info about my development envoroment:
I am using:
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
Rails 3.0.9
rvm
Gemfile snippet:
group :development, :test do
gem "rspec-rails", "~> 2.6"
gem 'webrat', '0.7.1'
gem 'spork', '0.9.0.rc9'
gem 'autotest-standalone'
gem 'autotest', '4.4.6'
gem 'autotest-rails-pure', '4.1.2'
gem 'factory_girl_rails', '1.0'
end
Udpates:
Code that creates #user
require 'spec_helper'
describe User do
# attributes for user object
before(:each) do
#attr = {
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar",
:first_name => "Example",
:last_name => "Examploid",
:dob => Date.new(1980,07,03),
:relationships_status => "Single",
:sex => "male",
:sexual_orientaion => "Woman",
:about_me => "All about me",
:school_id => 1,
:education_id => 1,
:occupation_id => 1,
:web_site => "example.com"
}
end
# Some other tests...
# ...
describe "password encryption" do
# create user
before(:each) do
#user = User.create!(#attr)
end
# Some other tests...
# ...
# Here is shown the same test code as above.
describe "New password generation" do
it "should generate new password" do
# I even tried to use factory_girl's method
# #user = Factory(:device_user)
user = #user
old_pwd_hash = user.password_hash
pwd = user.generate_and_set_new_password!
pwd.should_not be_nil
user.password_hash.should_not == old_pwd_hash
end
end
end
end
Some clarification:
I am not using stubbing of #user's methods in any of my tests.
I tried to do the same with factory_girl, and got the same test's fail.
Here user's Factory definition:
Factory.define :device_user, :class => User do |user|
user.password "password"
user.password_confirmation "password"
user.email "device_user#example.com"
user.first_name "Device"
user.last_name "User"
user.dob Date.new(2011,10,03)
user.relationships_status "Single"
user.sex "male"
user.sexual_orientaion "Woman"
user.about_me "All about Device User"
user.school_id 1
user.education_id 1
user.occupation_id 1
user.web_site "device.example.com"
user.facebook_id "face_test_id"
end

Related

Devise confirmation instructions test is not working

I am using request_confirmation_test.rb file from devise
confirmation test from devise
I have included the helper too but the file is showing errors on each line. Looks like something is missing that is necessary for the file to work correctly.This is the error that I am getting currently
ConfirmationInstructionsTest#test_set_up_sender_from_custom_mailer_defaults:
NoMethodError: undefined method `from' for nil:NilClass
test/integration/request_confirmation_test.rb:46:in `block in <class:ConfirmationInstructionsTest>'
this is my code for testing file
require 'test_helper'
class ConfirmationInstructionsTest < ActionMailer::TestCase
def setup
setup_mailer
Devise.mailer = 'Devise::Mailer'
Devise.mailer_sender = 'johnmax.rtc#gmail.com'
end
def teardown
Devise.mailer = 'Devise::Mailer'
Devise.mailer_sender = 'please-change-me#config-initializers-devise.com'
end
def user
#user ||= create_user
end
def mail
#mail ||= begin
user
ActionMailer::Base.deliveries.first
end
end
test 'email sent after creating the user' do
assert_not_nil mail
end
# test 'content type should be set to html' do
# assert mail.content_type.include?('text/html')
# end
test 'send confirmation instructions to the user email' do
mail
assert_equal [user.email], mail.to
end
test 'set up sender from configuration' do
assert_equal ['johnmax.rtc#gmail.com'], mail.from
end
test 'set up sender from custom mailer defaults' do
Devise.mailer = 'Users::Mailer'
assert_equal ['johnmax.rtc#gmail.com'], mail.from
end
# test 'set up sender from custom mailer defaults with proc' do
# Devise.mailer = 'Users::FromProcMailer'
# assert_equal ['johnmax.rtc#gmail.com'], mail.from
# end
# test 'custom mailer renders parent mailer template' do
# Devise.mailer = 'Users::Mailer'
# assert_present mail.body.encoded
# end
# test 'set up reply to as copy from sender' do
# assert_equal ['johnmax.rtc#gmail.com'], mail.reply_to
# end
# test 'set up reply to as different if set in defaults' do
# Devise.mailer = 'Users::ReplyToMailer'
# assert_equal ['johnmax.rtc#gmail.com'], mail.from
# assert_equal ['johnmax.rtc#gmail.com'], mail.reply_to
# end
test 'set up subject from I18n' do
store_translations :en, devise: { mailer: { confirmation_instructions: { subject: 'Account Confirmation' } } } do
assert_equal 'Account Confirmation', mail.subject
end
end
test 'subject namespaced by model' do
store_translations :en, devise: { mailer: { confirmation_instructions: { user_subject: 'User Account Confirmation' } } } do
assert_equal 'User Account Confirmation', mail.subject
end
end
test 'body should have user info' do
assert_match user.email, mail.body.encoded
end
test 'body should have link to confirm the account' do
host, port = ActionMailer::Base.default_url_options.values_at :host, :port
if mail.body.encoded =~ %r{<a href=\"http://#{host}:#{port}/users/confirmation\?confirmation_token=([^"]+)">}
assert_equal $1, user.confirmation_token
else
flunk "expected confirmation url regex to match"
end
end
# test 'renders a scoped if scoped_views is set to true' do
# swap Devise, scoped_views: true do
# assert_equal user.email, mail.body.decoded
# end
# end
# test 'renders a scoped if scoped_views is set in the mailer class' do
# begin
# Devise::Mailer.scoped_views = true
# assert_equal user.email, mail.body.decoded
# ensure
# Devise::Mailer.send :remove_instance_variable, :#scoped_views
# end
# end
test 'mailer sender accepts a proc' do
swap Devise, mailer_sender: proc { "another#example.com" } do
assert_equal ['another#example.com'], mail.from
end
end
end
This is the helper file
require 'active_support/test_case'
class ActiveSupport::TestCase
VALID_AUTHENTICATION_TOKEN = 'AbCdEfGhIjKlMnOpQrSt'.freeze
def setup_mailer
ActionMailer::Base.deliveries = []
end
def store_translations(locale, translations, &block)
# Calling 'available_locales' before storing the translations to ensure
# that the I18n backend will be initialized before we store our custom
# translations, so they will always override the translations for the
# YML file.
I18n.available_locales
I18n.backend.store_translations(locale, translations)
yield
ensure
I18n.reload!
end
def generate_unique_email
##email_count ||= 0
##email_count += 1
"test#{##email_count}#example.com"
end
def valid_attributes(attributes={})
{ first_name: "usertest",
email: generate_unique_email,
password: '12345678',
password_confirmation: '12345678' }.update(attributes)
end
def new_user(attributes={})
User.new(valid_attributes(attributes))
end
def create_user(attributes={})
User.create!(valid_attributes(attributes))
end
def create_admin(attributes={})
valid_attributes = valid_attributes(attributes)
valid_attributes.delete(:first_name)
Admin.create!(valid_attributes)
end
def create_user_without_email(attributes={})
UserWithoutEmail.create!(valid_attributes(attributes))
end
def create_user_with_validations(attributes={})
UserWithValidations.create!(valid_attributes(attributes))
end
# Execute the block setting the given values and restoring old values after
# the block is executed.
def swap(object, new_values)
old_values = {}
new_values.each do |key, value|
old_values[key] = object.send key
object.send :"#{key}=", value
end
clear_cached_variables(new_values)
yield
ensure
clear_cached_variables(new_values)
old_values.each do |key, value|
object.send :"#{key}=", value
end
end
def clear_cached_variables(options)
if options.key?(:case_insensitive_keys) || options.key?(:strip_whitespace_keys)
Devise.mappings.each do |_, mapping|
mapping.to.instance_variable_set(:#devise_parameter_filter, nil)
end
end
end
end
Any help would be highly appreciated.

how display current user in debug mode?

please help display current_user.
i use rails4 + devise + rspec + capybara. i try signin user.
test:
it 'check display username on top panel' do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
visit root_path
binding.pry
end
factories:
FactoryGirl.define do
factory :user do
sequence(:name){ |i| "us#{i}" }
sequence(:email){ |i| "us#{i}#ad.ad" }
password 'qwerty'
password_confirmation{ |u| u.password }
end
end
rails_helper:
..........
..............
RSpec.configure do |config|
...........
..........
config.include Devise::TestHelpers, :type => :controller
end
but I can not display the current user.
console output:
[1] pry(#<RSpec::ExampleGroups::AlbumsController>)> puts user.id
1
=> nil
[2] pry(#<RSpec::ExampleGroups::AlbumsController>)> puts user
#<User:0x000000086cee10>
=> nil
[3] pry(#<RSpec::ExampleGroups::AlbumsController>)> puts current_user
NameError: undefined local variable or method `current_user' for #<RSpec::ExampleGroups::AlbumsController:0x0000000863d348>
from /home/kalinin/.rvm/gems/ruby-2.0.0-p598/gems/rspec-expectations-3.3.0/lib/rspec/matchers.rb:966:in `method_missing'
[4] pry(#<RSpec::ExampleGroups::AlbumsController>)>
If you needs to check login user or not - try to call user_signed_in? helper after sign_in user
sorry for my bad English
You can use subject.current_user to access the current logged in user.

Why can't I create active records that count for something in rspec?

Why after I create records is the count zero ?
require 'spec_helper'
...
describe "keep admin" do
its "Can't delete the only admin" do
user1 = User.create(username: 'user1', password: 'abc123', admin: true)
user2 = User.create(username: 'user2', password: 'def456', admin: true)
user3 = User.create(username: 'user3', password: 'ghi798', admin: true)
#User.delete_me(user1) currently commented out to be sure.
#User.delete_me(user2)
#User.delete_me(user3)
expect(User.where(admin: true).count).to eq 3
end
end
1) keep admin Can't delete the only admin should eq 3
Failure/Error: expect(User.where(admin: true).count).to eq 3
expected: 3
got: 0
(compared using ==)
# ./spec/models/user_spec.rb:24:in `block (2 levels) in <top (required)>'
Finished in 0.41264 seconds
5 examples, 1 failure
$ RAILS_ENV=test rails c
User
Loading test environment (Rails 3.2.17)
2.0.0-p247 :001 > User
=> User(id: integer, username: string, pwd_hashed: string, salt: string,
created_at: datetime, updated_at: datetime, admin: boolean)
I can create users ok when using the app itself.
I tried create and create!. Why is the count always zero ?
Edit - full User model -
class User < ActiveRecord::Base
require 'digest/sha1'
attr_accessor :password_confirmation
attr_accessor :admin
validates_presence_of :username
validates_uniqueness_of :username
validates_confirmation_of :password
validate :password_non_blank
def self.delete_me(user)
how_many_admins = User.where(admin: true).count
if how_many_admins > 1
puts "delete ok!"
user.delete
else
puts "delete not ok!"
end
end
def self.authenticate(name, password)
user = self.find_by_username(name)
if user
expected_password = encrypted_password(password, user.salt)
if user.pwd_hashed != expected_password
user = nil
end
end
user
end
def password
#password
end
def password=(pwd)
#password = pwd
return if pwd.blank?
create_new_salt
self.pwd_hashed = User.encrypted_password(self.password, self.salt)
end
def is_admin
admin ? 'Yes' : 'No'
end
private
def password_non_blank
errors.add(:password, "Missing password") if pwd_hashed.blank?
end
def create_new_salt
self.salt = self.object_id.to_s + rand.to_s
end
def self.encrypted_password(password, salt)
string_to_hash = password + "wibble" + salt
Digest::SHA1.hexdigest(string_to_hash)
end
end
Your admin attribute isn't being persisted to the database (because of the call to attr_accessor), so counting users where admin is true will always return 0.
It also looks like your password confirmation should fail, since you're not actually providing a confirmation.

Create users in Factory Girl with OmniAuth?

I am currently creating an application that uses OmniAuth to create and authenticate users. I am encountering problems during testing due to Factory Girl being unable to generate users without OmniAuth.
I have several different ways to get factory girl to create users with omniauth but none have been successful.
I have added the following 2 lines to my spec_helper file
OmniAuth.config.test_mode = true \\ allows me to fake signins
OmniAuth.config.add_mock(:twitter, { :uid => '12345', :info => { :nickname => 'Joe Blow' }})
current factories.rb
FactoryGirl.define do
factory :user do
provider "twitter"
sequence(:uid) { |n| "#{n}" }
sequence(:name) { |n| "Person_#{n}" }
end
end
The following test currently fails because no user is being generated
let(:user) { FactoryGirl.create(:user) }
before { sign_in user }
describe "registering" do
it "should increment" do
expect do
click_button 'register'
end.to change(user.rounds, :count).by(1)
end
How should I change my factories/tests in order to get Factory Girl to create test users with OmniAuth?
Edit: I used the RailsCast guide to setup Omniauth,
#create function inside user.rb
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["name"]
end
end
hopefully also useful
#create inside the session_controller
def create
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
Did you remember to do the following somewhere in the test setup?
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
If you did, is it possible the user's UID doesn't match the mock uid?
You can try changing the factory definition from sequence(:uid) { |n| "#{n}" } to uid '12345'.

password Ruby on Rails Tutorial by M. Hartl

I've just started using rails, and decided to follow the "Ruby on Rails Tutorial" by M. Hartl. Seems like a good intro.
Am running into a failed test that's driving me nuts.
I am running rails 3.1.1, with rspec 2.7.0
I have tried modifying the condition, and tests on the "has_password" method work.
The failing test:
1) User password encryption authenticate method should return the user on email/password match
Failure/Error: matching_user.should == #user
expected: #
got: nil (using ==)
# ./spec/models/user_spec.rb:149:in `block (4 levels) in '
The rspec test:
describe User do
before(:each) do
#attr = {:name => 'testing',
:email =>'testing#example.com',
:password => "testtest",
:password_confirmation => "testtest"}
end
...
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
...
describe "authenticate method" do
it "should exist" do
User.should respond_to(:authenticate)
end
it "should return nil on email/password mismatch" do
User.authenticate(#attr[:email], "wrongpass").should be_nil
end
it "should return nil for an email address with no user" do
User.authenticate("bar#foo.com", #attr[:password]).should be_nil
end
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
In the User model:
...
def has_password?(submitted_password)
encrypt_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email) #self.where("email = ?", email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
I cannot figure out what I'm doing wrong here.
In your failing spec you have
matching_user.should == #user
But #user isn't defined anywhere so it's set to nil.
Edit:
Try adding the following puts into the failing spec and see what results you get in your spec output after running it.
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
puts matching_user # add this
puts #user # and also this
matching_user.should == #user
end

Resources