Failing test in ruby tutorial (Michael Hartl) - ruby-on-rails-3

I'm studying Michael Hartl's tutorial. I'm using RSPEC to run the test.
So far so good but it seems that I've hit the wall with the following example.
Here is the test that fails (it should pass):
describe "authenticate method" do
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
Just in case.
#user defined as:
before(:each) do
#user = User.create!(#attr)
end
#attr defined as:
before(:each) do
#attr = {
:name => "Example user",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
Entries in user.rb
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
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
private
def encrypt_password
self.salt = make_salt unless has_password?(password)
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
Error message displayed when the test is failing
c:\RailsInstaller\work\apptwit>rspec spec/models/user_spec.rb
.................F
Failures:
1) User password validations password encryption authenticate method should return the user on email/password
Failure/Error: matching_user.should == #user
expected: #<User id: 1, name: "Example user", email: "user#example.com", created_at: "2011-12-07 19:08:23
ed_at: "2011-12-07 19:08:23", encrypted_password: "fbdbaf712fa1b6c925c4ab2192e73ac9f9d1bedf67630610d68...">
got: nil (using ==)
# ./spec/models/user_spec.rb:204:in `block (5 levels) in <top (required)>'
Finished in 221.37 seconds
18 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:202 # User password validations password encryption authenticate method should
he user on email/password match
I would appreciate any pointers,
Thanks a lot.

If matching_user is nil, then you might want to put some puts email and puts user.inspect statements in self.authenticate to debug it.
It seems as though it's either not able to find the user by email or your password is incorrect for some reason in the authenticate method.

Related

Hartl test errors - redirect when (not) logged in as wrong user, not allow admin edited

I have a few errors that I can't resolve. All the tests pass. If there are errors, I only need to check the test, right? I only need to check the code if they fail, right?
Below are the errors:
Error:
UsersControllerTest#test_should_redirect_update_when_logged_in_as_wrong_user:
ArgumentError: wrong number of arguments (given 2, expected 1)
test/controllers/users_controller_test.rb:45:in `block in '
bin/rails test test/controllers/users_controller_test.rb:43
Error:
UsersControllerTest#test_should_not_allow_the_admin_attribute_to_be_edited_via_the_web:
ArgumentError: unknown keywords: id, user
test/controllers/users_controller_test.rb:36:in `block in '
bin/rails test test/controllers/users_controller_test.rb:33
Error:
UsersControllerTest#test_should_redirect_index_when_not_logged_in:
URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80index
test/controllers/users_controller_test.rb:11:in `block in '
bin/rails test test/controllers/users_controller_test.rb:10
Error:
PasswordResetsTest#test_password_resets:
NameError: undefined local variable or method 'expired' for #
test/integration/password_resets_test.rb:62:in `block in '
bin/rails test test/integration/password_resets_test.rb:10
Below are my files:
test/integration/password_resets_test.rb
require 'test_helper'
class PasswordResetsTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
#user = users(:michael)
end
test "password resets" do
get new_password_reset_path
assert_template 'password_resets/new'
# Invalid email
post password_resets_path, params: { password_reset: { email: "" } }
assert_not flash.empty?
assert_template 'password_resets/new'
# Valid email
post password_resets_path,
params: { password_reset: { email: #user.email } }
assert_not_equal #user.reset_digest, #user.reload.reset_digest
assert_equal 1, ActionMailer::Base.deliveries.size
assert_not flash.empty?
assert_redirected_to root_url
# Password reset form
user = assigns(:user)
# Wrong email
get edit_password_reset_path(user.reset_token, email: "")
assert_redirected_to root_url
# Inactive user
user.toggle!(:activated)
get edit_password_reset_path(user.reset_token, email: user.email)
assert_redirected_to root_url
user.toggle!(:activated)
# Right email, wrong token
get edit_password_reset_path('wrong token', email: user.email)
assert_redirected_to root_url
# Right email, right token
get edit_password_reset_path(user.reset_token, email: user.email)
assert_template 'password_resets/edit'
assert_select "input[name=email][type=hidden][value=?]", user.email
# Invalid password & confirmation
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "barquux" } }
assert_select 'div#error_explanation'
# Empty password
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "",
password_confirmation: "" } }
assert_select 'div#error_explanation'
# Valid password & confirmation
patch password_reset_path(user.reset_token),
params: { email: user.email,
user: { password: "foobaz",
password_confirmation: "foobaz" } }
assert is_logged_in?
assert_not flash.empty?
assert_redirected_to user
#passes w percent now, may need to change
assert_match(/%#{expired}/i, response.body)
end
end
test/controllers/users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
#other_user = users(:archer)
end
test "should redirect index when not logged in" do
get :index
assert_redirected_to login_url
end
test "should get new" do
get signup_path
assert_response :success
end
test "should redirect edit when not logged in" do
get edit_user_path(#user)
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch user_path(#user), params: { user: { name: #user.name,
email: #user.email } }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should not allow the admin attribute to be edited via the web" do
log_in_as(#other_user)
assert_not #other_user.admin?
patch :update, id: #other_user, user: { password: #other_user.password,
password_confirmation: #other_user.password_confirmation,
admin: true }
assert_not #other_user.reload.admin?
end
test "should redirect update when logged in as wrong user" do
log_in_as(#other_user)
patch :update, user_path(#user), user: { name: #user.name, email: #user.email }
assert flash.empty?
assert_redirected_to root_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert_redirected_to login_url
end
test "should redirect destroy when logged in as a non-admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert_redirected_to root_url
end
end
Below are supporting files:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
# Shows all users, delete for sups app but useful for BRBBaby
def index
#users = User.where(activated: true).paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
redirect_to root_url and return unless :active
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update]
def new
end
def create
#user = User.find_by(email: params[:password_reset][:email].downcase)
if #user
#user.create_reset_digest
#user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty? # Case (3)
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params) # Case (4)
log_in #user
#user.update_attribute(:reset_digest, nil)
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit' # Case (2)
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
def get_user
#user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (#user && #user.activated? &&
#user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if #user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += "Check your email for the activation link."
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# Returns true if the given user is the current user.
def current_user?(user)
user == current_user
end
# Returns the current logged-in user (if any).
def current_user
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
#current_user = user
end
end
end
# Returns true if the user is logged in, false otherwise.
# FIXES - this was !current_user.nil? and had lots of errors and was fixed with below, but not sure right now how it might affect other parts of app.
def logged_in?
!current_user.nil?
end
# Forgets a persistent session.
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user.
def log_out
forget(current_user)
session.delete(:user_id)
#current_user = nil
end
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
end
module UsersHelper
# Returns the Gravatar for the given user.
def gravatar_for(user, options = { size: 80 })
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
size = options[:size]
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /[A-Z0-9._%+-]+#(?:[A-Z0-9-]+\.)+[A-Z]{2,}/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_columns(activated: true, activated_at: Time.zone.now)
# The above line should count for the below two lines
# update_attribute(:activated, true)
# update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
require 'test_helper'
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
end
test "unsuccessful edit" do
log_in_as(#user)
get edit_user_path(#user)
assert_template 'users/edit'
patch user_path(#user), params: { user: { name: "",
email: "foo#invalid",
password: "foo",
password_confirmation: "bar" } }
assert_template 'users/edit'
end
test "successful edit with friendly forwarding" do
get edit_user_path(#user)
log_in_as(#user)
assert_redirected_to edit_user_path(#user)
name = "Foo Bar"
email = "foo#bar.com"
patch user_path(#user), params: { user: { name: name,
email: email,
password: "",
password_confirmation: "" } }
assert_not flash.empty?
assert_redirected_to #user
#user.reload
assert_equal name, #user.name
assert_equal email, #user.email
end
end
require 'test_helper'
class UsersIndexTest < ActionDispatch::IntegrationTest
def setup
#admin = users(:michael)
#non_admin = users(:archer)
end
test "index as admin including pagination and delete links" do
log_in_as(#admin)
get users_path
assert_template 'users/index'
assert_select 'div.pagination'
first_page_of_users = User.paginate(page: 1)
first_page_of_users.each do |user|
assert_select 'a[href=?]', user_path(user), text: user.name
unless user == #admin
assert_select 'a[href=?]', user_path(user), text: 'delete'
end
end
assert_difference 'User.count', -1 do
delete user_path(#non_admin)
end
end
test "index as non-admin" do
log_in_as(#non_admin)
get users_path
assert_select 'a', text: 'delete', count: 0
end
end
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
end
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
# Validations may have been cheated; confirm platonic result
test "login with valid information followed by logout" do
get login_path
post login_path, params: { session: { email: #user.email,
password: 'password' } }
assert is_logged_in?
assert_redirected_to #user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(#user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
# Simulate a user clicking logout in a second window.
delete logout_path
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(#user), count: 0
end
test "login with remembering" do
log_in_as(#user, remember_me: '1')
assert_not_nil cookies['remember_token']
end
test "login without remembering" do
# Log in to set the cookie.
log_in_as(#user, remember_me: '0')
# Log in again and verify that the cookie is deleted.
assert_nil cookies['remember_token']
end
end
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user#example.com",
password: "password",
password_confirmation: "password" } }
end
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
# Try to log in before activation.
log_in_as(user)
assert_not is_logged_in?
# Invalid activation token
get edit_account_activation_path("invalid token", email: user.email)
assert_not is_logged_in?
# Valid token, wrong email
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
# Valid activation token
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
end
end
require 'test_helper'
#######
## Amp up password security in the future
## https://www.google.com/search?q=rails+enforce+password+strength
######
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
test "should be valid" do
assert #user.valid?
end
test "name should be present" do
#user.name = " "
assert_not #user.valid?
end
test "email should be present" do
#user.email = " "
assert_not #user.valid?
end
test "name should not be too long" do
#user.name = "a" * 51
assert_not #user.valid?
end
test "email should not be too long" do
#user.email = "a" * 244 + "#example.com"
assert_not #user.valid?
end
test "email validation should accept valid addresses" do
valid_addresses = %w[user#example.com USER#foo.COM A_US-ER#foo.bar.org
first.last#foo.jp alice+bob#baz.cn]
valid_addresses.each do |valid_address|
#user.email = valid_address
assert #user.valid?, "#{valid_address.inspect} should be valid"
end
end
test "email validation should reject invalid addresses" do
invalid_addresses = %w[user#example,com user_at_foo.org user.name#example.
foo#bar_baz.com foo#bar+baz.com]
invalid_addresses.each do |invalid_address|
#user.email = invalid_address
assert_not #user.valid?, "#{invalid_address.inspect} should be invalid"
end
end
test "email addresses should be unique" do
duplicate_user = #user.dup
duplicate_user.email = #user.email.upcase
#user.save
assert_not duplicate_user.valid?
end
test "password should be present (nonblank)" do
#user.password = #user.password_confirmation = " " * 6
assert_not #user.valid?
end
test "password should have a minimum length" do
#user.password = #user.password_confirmation = "a" * 5
assert_not #user.valid?
end
test "authenticated? should return false for a user with nil digest" do
assert_not #user.authenticated?(:remember, '')
end
end
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/reporters"
Minitest::Reporters.use!
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Returns true if a test user is logged in.
def is_logged_in?
!session[:user_id].nil?
end
# Logs in a test user.
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, params: { session: { email: user.email,
password: password,
remember_me: remember_me } }
else
session[:user_id] = user.id
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
Any help would be appreciated. Let me know if there is any info I can provide or if you have questions for me. Thank you for your help. Cheers.
1)
Error: UsersControllerTest#test_should_redirect_update_when_logged_in_as_wrong_user: ArgumentError: wrong number of arguments (given 2, expected 1) test/controllers/users_controller_test.rb:45:in `block in ' bin/rails test test/controllers/users_controller_test.rb:43
For this one you've used patch :update user_path(#user)... when you should just have patch user_path(#user)...
2)
Error: UsersControllerTest#test_should_not_allow_the_admin_attribute_to_be_edited_via_the_web: ArgumentError: unknown keywords: id, user test/controllers/users_controller_test.rb:36:in `block in ' bin/rails test test/controllers/users_controller_test.rb:33
This is because you have used patch :update id: #othr_user ... instead of patch user_path(#other_user) ...
3)
Error: UsersControllerTest#test_should_redirect_index_when_not_logged_in: URI::InvalidURIError: bad URI(is not URI?): http://www.example.com:80index test/controllers/users_controller_test.rb:11:in `block in ' bin/rails test test/controllers/users_controller_test.rb:10
http://www.example.com:80index is indeed a bad URL and should be http://www.example.com:80/index
I'd be guessing that login_url is setup incorrectly in your routes file... have a look at what it is and try to add the missing /?
EDIT: Actually I think there might be something else here
You have get :index where all the other tests use an actual test path eg get users_path.
4)
Error: PasswordResetsTest#test_password_resets: NameError: undefined local variable or method 'expired' for # test/integration/password_resets_test.rb:62:in `block in ' bin/rails test test/integration/password_resets_test.rb:10
This line: assert_match(/%#{expired}/i, response.body) contains the local variable expired but nowhere have you assigned a value to expired... what do you intend for this value to be?

why controller var isn't available within a test?

I'm trying to follow Michael Hartl's Ruby on Rails Tutorial in http://ruby.railstutorial.org/chapters/sign-in-sign-out, but with some changes to practice, above all, some variations and the Test::Unit framework. In the tutorial, RSpec is used, while I'm trying to stick to Test::Unit + Shoulda-context.
In chapter 9 I'm suposed to pass some functional tests that use a var called 'controller', but my tests don't work as they find out that 'controller' doesn't exist. This is what I get:
marcel#pua:~/Desenvolupament/Rails3Examples/ror_tutorial$ rake
test:recent Loaded suite
/home/marcel/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started F
=============================================================================== Failure: test: POST 'create' with valid signin (email and password)
should redirect to the user show page. (SessionsControllerTest)
[test/functional/sessions_controller_test.rb:58]: Expected at least 1
element matching "title", found 0. is not true.
=============================================================================== E
=============================================================================== Error: test: POST 'create' with valid signin (email and password)
should sign in the user. (SessionsControllerTest): NameError:
undefined local variable or method `controller' for
test/functional/sessions_controller_test.rb:53:in `block (3 levels) in <class:SessionsControllerTest>'
=============================================================================== Finished in 0.957865676 seconds. 7 tests, 6 assertions, 1 failures, 1
errors, 0 pendings, 0 omissions, 0 notifications 0% passed
7.31 tests/s, 6.26 assertions/s rake aborted! Command failed with status (1): [/home/marcel/.rvm/rubies/ruby-1.9.2-p290/b...] Tasks: TOP
=> test:recent (See full trace by running task with --trace)
This is the original (RSpec) test:
describe SessionsController do
...
describe "POST 'create'" do
...
describe "with valid email and password" do
before(:each) do
#user = Factory(:user)
#attr = { :email => #user.email, :password => #user.password }
end
it "should sign the user in" do
post :create, :session => #attr
controller.current_user.should == #user
controller.should be_signed_in
end
it "should redirect to the user show page" do
post :create, :session => #attr
response.should redirect_to(user_path(#user))
end
end
end
end
and this is my translated (into Test::Unit + Sholuda-context) test:
class SessionsControllerTest < ActionController::TestCase
context "POST 'create'" do
context "with valid signin (email and password)" do
setup do
#attr = {email: "test#email.tst", password: "testpwd"}
#user=User.create! #attr.merge!({name: "test_user", password_confirmation: "testpwd"})
end
should "sign in the user" do
post :create, :session => #attr
assert_equal #user, controller.current_user
end
should "redirect to the user show page" do
post :create, :session => #attr
assert_select "title", /Show/
end
end
end
end
Has anybody any idea how to make my test work?
Looking at the official Rails testing guide at http://guides.rubyonrails.org/testing.html, I've seen that an instance variable called #controller is enabled in functional tests. so, the Test::Unit version should be:
should "sign in the user" do
post :create, :session => #attr
assert_equal #user, #controller.current_user
end

Ruby Problemewith filter and RSpec

I can't figure out why this RSpec test fails. Any advice?
I try to handle the destruction of post. Only users who create posts can delete them.
class PostsController < ApplicationController
before_filter :authorized_user, :only => :destroy
def destroy
post = Post.find(params[:id])
post.destroy
redirect_to posts_path, notice: 'Post successfully destroyed'
end
private
def authorized_user
redirect_to posts_path, notice: 'Access Denied' if current_user.posts.find_by_id(params[:id]).nil?
end
Test :
describe "DELETE destroy" do
before(:each) do
#post = stub_model(Post, :id => 23)
#post.stub(:destroy){true}
Post.stub(:find){#post}
end
it "should search the post" do
Post.should_receive(:find).with(#post.id.to_s).and_return(#post)
delete :destroy, {:id => #post.id }
end
it "should destroy the post" do
#post.should_receive(:destroy)
delete :destroy, {:id => #post.id }
end
it "should redirect to the posts list" do
delete :destroy, {:id => #post.id }
response.should redirect_to posts_path
end
end
And errors :
1) PostsController DELETE destroy should search the post
Failure/Error: Post.should_receive(:find).with(#post.id.to_s).and_return(#post)
(<Post(id: integer, title: string, body: text, created_at: datetime, updated_at: datetime, user_id: integer) (class)>).find("23")
expected: 1 time
received: 0 times
# ./spec/controllers/posts_controller_spec.rb:67:in `block (3 levels) in <top (required)>'
I think it is because in the before(:each) block you have to log in a user. You have before_filter and this filter is only for :destroy. So when you don't have a logged in user then your tests will fail because it is actually a user without permissions that "runs the test". So you should put this code in before(:each):
user = way_to_make_user - I do it with: FactoryGirl.create(:user,role=>"client")
controller.sign_in user
I could help you more if you tell me what do you use for authentication. I use cancan gem. So the main point is that you have to log in user before each test.

Rails 3 + Rspec 2: Validation failed: Email has already been taken

I have 2 models, User and Bucket. User has_many Buckets and a Bucket belongs_to a User.
In factories.rb, I have:
Factory.define :user do |user|
user.email "teste#test.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.sequence :email do |n|
"person-#{n}#example.com"
end
Factory.define :bucket do |bucket|
bucket.email "user#example.com"
bucket.confirmation false
bucket.association :user
end
and I have a login_user module as follows:
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
##user.confirm!
sign_in #user
end
end
I am using Spork and Watch and my Buckets_controller_spec.rb is as simple as:
describe "User authenticated: " do
login_user
#bucket = Factory(:bucket)
it "should get index" do
get 'index'
response.should be_success
end
...
end
The error is always the same:
Failures:
1) BucketsController User authenticated: should get index
Failure/Error: Unable to find matching line from backtrace
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./lib/controller_macros.rb:12:in `block in login_user'
And it only happens when I have the Factory(:bucket). The login works fine when I don't add the Factory(:bucket).
It's always the same error. I have tried adding :email => Factory.next(:email) to the user, but no success.
Edit:
In rails c test:
ruby-1.9.2-p180 :019 > bucket = Factory(:bucket, :email => "hello#hello.com")
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken
ruby-1.9.2-p180 :018 > Bucket.create(:email => "hello#hello.com")
=> #<Bucket id: 2, email: "hello#hello.com", confirmation: nil, created_at: "2011-04-08 21:59:12", updated_at: "2011-04-08 21:59:12", user_id: nil>
Edit 2:
I found out that the error is in the association, however, I don't know how to fix it.
bucket.association :user
When you define a factory with an association you need to give the factory an object to associate with whenever you use the factory.
This should work:
describe "User authenticated: " do
login_user
#bucket = Factory(:bucket, :user => #user)
it "should get index" do
get 'index'
response.should be_success
end
end
That way factorygirl knows to make a bucket which is associated with #user.
Try this in your user factory:
Factory.define :user do |f|
f.sequence(:email) { |n| "test#{n}#example.com" }
...
end
I think that's probably your problem. When you use f.email = "anyvalue" it's going to use that value every time. I see you were trying to create a sequence in the next block, but I'm not sure that sequence is getting used.
ALSO - be aware that if you get tests interrupted by a crash or something, sometimes bogus test data can get left in your test DB instead of being rolled back.
Very first thing I try if something worked once and then quit working is to reset the test db.
rake db:test:prepare
That will clean everything out.
If this doesn't work let me know and I'll take a second look!
If someone is getting this recently with your views. Try using Database Cleaner.
For more info: RailsTutorial - chapter 8.4.3 - Test database not clearing after adding user in integration test

Why is capybara/rspec losing the current_user?

The following spec is failing and failing over again. I've tried everything but can't get it to work. If I test it manually all looks fine :( Some help/tips would be really nice!
The join action should add an logged-in user to a guild/group if he/she has the right token (in the URL). If the user isn't logged in, the action redirects to the login page and saves the token and the ID to cookies. After login the user gets redirected to the join page if the cookies are set.
I've found out that the current_user get lost during the test. The session variable is still present. I've a standard Authlogic setup and all other tests are passing so I really don't know what's going wrong. I'm new to RSpec/capybara but the cucumber/capybara test (from which I'm migrating) is also failing so I think it's a capybara issue.
Failing Spec:
describe GuildsController do
fixtures :roles
def login
#user = Factory(:User)
visit login_path
fill_in 'Login', :with => #user.login
fill_in 'Password', :with => 'password'
click 'Login'
page.should have_css(".notice")
end
def assing_user_to_guild_as(role)
role_id = Role.where(:name => role).first.id
#guild.assignments << Assignment.new(:role_id => role_id, :user_id => #user.id, :guild_id => #guild.id)
end
before(:each) do
#guild = Guild.first || Factory(:Guild).build
visit root_path
end
context "a user" do
before(:each) do
login
end
it "should be able to join a guild with a valid token" do
visit "guilds/#{#guild.id}/join/#{#guild.token}"
#guild.members.include?(#user.login).should be_true
page.should have_css(".notice")
end
it "shouldn't be able to join a guild with a invalid token" do
visit "guilds/#{#guild.id}/join/#{#guild.token+"invalid"}"
#guild.members.include?(#user.login).should be_false
page.should have_css(".error")
end
end
end
Controller Action:
def join
#guild = Guild.find(params[:id])
respond_to do |format|
if current_user.nil?
flash[:error] = t("have_to_be_logged_in")
unless params[:token].nil?
cookies[:rguilds_jg_token] = params[:token]
cookies[:rguilds_jg_gid] = params[:id]
end
format.html { redirect_to(login_path) }
else
unless cookies[:rguilds_jg_token].nil? && cookies[:rguilds_jg_gid].nil?
cookies.delete(:rguilds_jg_token)
cookies.delete(:rguilds_jg_gid)
end
if #guild.verified?
if params[:token] == #guild.token
unless #guild.users.include?(current_user)
#guild.assignments << Assignment.create(:user_id => current_user.id, :role_id => Role.find_by_name("member").id)
flash[:notice] = t('guilds.joined')
format.html { redirect_to(#guild) }
else
flash[:error] = t('guilds.already_joined')
format.html { redirect_to(#guild) }
end
else
flash[:error] = t('guilds.invalid_token')
format.html { redirect_to(#guild) }
end
else
flash[:error] = t('guilds.not_verified')
format.html { redirect_to(#guild) }
end
end
end
end
"rake spec" result:
...................FF.....................................................................
Failures:
1) GuildsController a user should be able to join a guild with a valid token
Failure/Error: #guild.members.include?(#user.login).should be_true
expected false to be true
# ./spec/integration/guilds_spec.rb:72:in `block (3 levels) in <top (required)>'
2) GuildsController a user shouldn't be able to join a guild with a invalid token
Failure/Error: page.should have_css(".error")
expected #has_css?(".error") to return true, got false
# ./spec/integration/guilds_spec.rb:79:in `block (3 levels) in <top (required)>'
Finished in 7.87 seconds
90 examples, 2 failures
Gems:
gem 'rails', '3.0.0.rc'
gem "mocha"
gem "rspec-rails", ">= 2.0.0.beta.19"
gem "factory_girl_rails"
gem 'capybara'
gem "authlogic", :git => "http://github.com/odorcicd/authlogic.git", :branch => "rails3"
# In your test_helper.rb / spec_helper.rb
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
# Forces all threads to share the same connection. This works on
# Capybara because it starts the web server in a thread.
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
This is from http://gist.github.com/470808