I am creating a Rails application with TDD using rspec. I am getting an error I can't remove :
Failure/Error: invalid_user = User.new(#attr.merge("provider" => ""))
ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: uid, info
Here is my user spec :
user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
#attr = {"provider" => "providerexample", "uid" => "uidexample", "info" => {"name" => "Example"}}
end
it "should create a new instance given valid attributes" do
user = User.create_with_omniauth(#attr)
end
it "should require a provider" do
invalid_user = User.new(#attr.merge("provider" => ""))
invalid_user.should_not be_valid
end
it "should require a uid" do
invalid_user = User.new(#attr.merge("uid" => ""))
invalid_user.should_not be_valid
end
end
And my user.rb
class User < ActiveRecord::Base
attr_accessible :name, :credits, :email, :provider
validates :name, :provider, :uid, :presence => true
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["name"]
end
end
end
If I debug the mass-assign error by adding uid and info to the attr_accessible, I still get the following error unknown attribute: info.
If you merge what you had as #attr with info, then it'll exist for the create_with_omniauth call, but not the regular create methods.
describe User do
let(:user_attributes) { {"provider" => "providerexample", "uid" => "uidexample"} }
it "should create a new instance given valid attributes" do
expect {
User.create_with_omniauth(user_attributes.merge({"info" => {"name" => "example"}))
}.to not_raise_error
end
end
Related
I am trying to test the update action of a devise Model in my app.
My factories.rb file:
FactoryGirl.define do
factory :student do
first_name "John"
last_name "Doe"
sequence(:email) { |n| "#{first_name}.#{last_name}#{n}#example.com".downcase }
city "Dhaka"
area "Mirpur"
zip 1216
full_address "Mirpur, Dhaka"
password "password"
password_confirmation "password"
confirmed_at Date.today
end
end
The rspec file:
require 'rails_helper'
RSpec.describe Students::RegistrationsController do
context "Student logged in" do
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:student]
#student = FactoryGirl.create(:student)
#updated_attributes = { :first_name => "New", :last_name => "Name" }
sign_in #student
put :update, :id => #student.id, :student => #updated_attributes
#student.reload
end
it { expect(#student.first_name).to eql "New" }
it { expect(#student.last_name).to eql "Name" }
end
end
I expected the tests to pass. But they are failing. The failing messages look like this:
Failure/Error: it { expect(#student.first_name).to eql "New" }
expected: "New"
got: "John"
(compared using eql?)
Failure/Error: it { expect(#student.last_name).to eql "Name" }
expected: "Name"
got: "Doe"
(compared using eql?)
So, basically, the attributes are not getting updated. What do i need to do to make rspec update the attributes and make the tests green?
I forgot to include the :current_password attribute in the #updated_attribute hash. Devise requires you to enter your current password in the edit form. So, #updated_attribute should be,
#updated_attributes = FactoryGirl.attributes_for(:student, :first_name => "New", :last_name => "Name", :current_password => "password" )
I have the following route:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks",
:registrations => 'users/registrations',
:sessions => "users/sessions" }
and the following controller test (registrations_controller_spec.rb):
require File.dirname(__FILE__) + '/../spec_helper'
describe Users::RegistrationsController do
include Devise::TestHelpers
fixtures :all
render_views
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
describe "POST 'create'" do
describe "success" do
before(:each) do
#attr = { :email => "user#example.com",
:password => "foobar01", :password_confirmation => "foobar01", :display_name => "New User" }
end
it "should create a user" do
lambda do
post :create, :user => #attr
response.should redirect_to(root_path)
end.should change(User, :count).by(1)
end
end
end
describe "PUT 'update'" do
before(:each) do
#user = FactoryGirl.create(:user)
#user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in #user
end
describe "Success" do
it "should change the user's display name" do
#attr = { :email => #user.email, :display_name => "Test", :current_password => #user.password }
put :update, :id => #user, :user => #attr
puts #user.errors.messages
#user.display_name.should == #attr[:display_name]
end
end
end
end
Now, when I run rspec spec I get (what I think) are weird results:
The "should create a user" test passes. The user count has increased by 1.
However, my "should change the user's display name" fails as follows:
1) Users::RegistrationsController PUT 'update' Success should change the user's display name
Failure/Error: #user.display_name.should == #attr[:display_name]
expected: "Test"
got: "Boyd" (using ==)
And the weird bit is that my statement:
puts #user.errors.messages
Renders the following message:
{:email=>["was already confirmed, please try signing in"]}
What's going on? The user is signed in! That's proved by the fact that the Rspec error returned the display_name of "Boyd". And why is it displaying a message looks as though it's related with account confirmation as opposed to updating a user's details?
Any help would be greatly appreciated!
This works. Thanks holtkampw for seeing what I wasn't! I put some extra code in there just to double check and everything is well!
it "should change the user's display name" do
subject.current_user.should_not be_nil
#attr = { :email => #user.email, :display_name => "Test", :current_password => #user.password }
puts "Old display name: " + subject.current_user.display_name
put :update, :id => subject.current_user, :user => #attr
subject.current_user.reload
response.should redirect_to(root_path)
subject.current_user.display_name == #attr[:display_name]
puts "New display name: " + subject.current_user.display_name
end
Well I've searched all over the place and it seems that nobody else has the issue with this 'encrypt' method causing their tests to fail, though it seems plenty others have had some difficulty with chapter 7. Without further adieu,
Here's the link to Hartl's Chapter 7
My code in the user model file, and the corresponding spec file, appear to be completely exact to what he has written and I still cannot get the tests to pass. The errors?
Failures:
1) User should create a new instance given valid attributes
Failure/Error: User.Create!(#attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (2 levels) in <top (required)>'
2) User should not allow duplicate email addresses
Failure/Error: User.Create!(#attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (2 levels) in <top (required)>'
3) User should reject email addresses identical up to case
Failure/Error: User.Create!(#attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (2 levels) in <top (required)>'
...
7) User has_password? method should be false if passwords do not match
Failure/Error: User.Create!(#attr)
NoMethodError: undefined method 'encrypt' for #<User:asdf>
#./app/models/user.rb:22:in 'has_password?'
#./app/models/user.rb:28:in 'encrypt_password'
#./spec/models/user_spec.rb:15:in 'block (3 levels) in <top (required)>'
so i'm getting the same error messages for each test and I am going nuts trying to find out why!
Here's my user.rb:
require 'digest'
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
#automatically create the virtual attribute for 'password_confirmation'
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
#returns true if the users password matches the submitted one
def has_password?(submitted_password)
encrypted_password == encrypt(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
end
and my user_spec.rb file:
require 'spec_helper'
require 'digest'
describe User do
before(:each) do
#attr = {
:name => "User Name",
:email => "example#email.com",
:password => "password",
:password_confirmation => "password"
}
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
it "should require a name" do
no_name_user = User.new(#attr.merge(:name => ""))
no_name_user.should_not be_valid
end
it "should require an email" do
no_email_user = User.new(#attr.merge(:email => ""))
no_email_user.should_not be_valid
end
it "should reject names that are too long" do
long_name = "a" * 51
long_name_user = User.new(#attr.merge(:name => long_name))
long_name_user.should_not be_valid
end
it "should accept valid email addresses" do
addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
addresses.each do |address|
valid_email_user = User.new(#attr.merge(:email => address))
valid_email_user.should be_valid
end
end
it "should reject invalid email addresses" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.]
addresses.each do |address|
invalid_email_user = User.new(#attr.merge(:email => address))
invalid_email_user.should_not be_valid
end
end
it "should not allow duplicate email addresses" do
User.create!(#attr)
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
it "should reject email addresses identical up to case" do
upcased_email = #attr[:email].upcase
User.create!(#attr.merge(:email => upcased_email))
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
describe "password validations" do
it "should require a password" do
User.new(#attr.merge(:password => "", :password_confirmation => ""))
should_not be_valid
end
it "should require password to match the password confirmation" do
User.new(#attr.merge(:password_confirmation => "invalid"))
should_not be_valid
end
it "should reject short passwords" do
short = "a" * 5
hash = #attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should reject long passwords" do
long = "a" * 41
hash = #attr.merge(:password => long, :password_confirmation => long)
User.new(hash).should_not be_valid
end
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should not allow a blank encrypted password" do
#user.encrypted_password.should_not be_blank
end
end
describe "has_password? method" do
before(:each) do
#attr = User.create!(#attr)
end
it "should be true if the passwords match" do
#user.has_password?(#attr[:password]).should be_true
end
it "should be false if the passwords don't match" do
#user.has_password?("invalid").should be_false
end
end
end
Any help would be greatly appreciated. I've poured over other's problems, my code, and changed various aspects to try and get the tests to work, all to no avail. I hope it's not something really stupid I'm still not seeing.
Your error is here:
def encrypt_string
secure_hash("#{salt}--#{string}")
end
You are calling encrypt in the following encrypt_password method but your method above is named encrypt_string:
def encrypt_password
self.salt = make_salt unless has_password?(password)
self.encrypted_password = encrypt(password)
end
Just change encrypt_string to encrypt in the method definition and you should be good to go.
I'm working through Hartl's book and I'm up to chapter 8. I've written some tests that I believe should be passing. I've quadruple checked my code against what's in the book, and double checked it against what's in the book's github repo, but I'm stumped. I'm getting the following errors from RSpec:
Failures:
1) UsersController POST 'create' should redirect to the user "show" page
Failure/Error: response.should redirect_to(user_path(assigns(:user)))
ActionController::RoutingError:
No route matches {:action=>"show", :controller=>"users", :id=>#<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, encrypted_password: nil, salt: nil>}
# ./spec/controllers/users_controller_spec.rb:93:in `block (3 levels) in <top (required)>'
2) UsersController POST 'create' should have a welcome message
Failure/Error: flash[:success].should =~ /welcome to the sample app/i
expected: /welcome to the sample app/i
got: nil (using =~)
# ./spec/controllers/users_controller_spec.rb:98:in `block (3 levels) in <top (required)>'
Finished in 0.83875 seconds
46 examples, 2 failures
Like I said, I've checked the code again and again. Restarted spork, restarted rails server, ran without spork. I've checked it against the code in the book and in the github repo. I've even copy/pasted the spec and controller code in the github repo, but all to no avail.
I'm stumped. It's late and I need to crash. :)
Hopefully one of you guys can see something I'm not. Here's what I've got so far...
users_controller_spec.rb
require 'spec_helper'
describe UsersController do
render_views
# ...
describe "POST 'create'" do
# ...
describe 'success' do
before(:each) do
#attr = { :name => 'New User', :email => 'some-email#gmail.com', :password => 'foobar', :password_confirmation => 'foobar' }
end
it 'should create a new user' do
lambda do
post :create, :user => #attr
end.should change(User, :count).by(1)
end
end
it 'should redirect to the user "show" page' do
post :create, :user => #attr
response.should redirect_to(user_path(assigns(:user)))
end
it 'should have a welcome message' do
post :create, :user => #attr
flash[:success].should =~ /welcome to the sample app/i
end
end
end
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#title = 'Sign up'
end
def show
#user = User.find params[:id]
#title = #user.name
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = 'Welcome to the Sample App!'
redirect_to #user
else
#title = 'Sign up'
render 'new'
end
end
end
user.rb
class User < ActiveRecord::Base
# Virtual properties (don't exist in db)
attr_accessor :password
# Accessible properties
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
# Return true if the user's password matches the submitted password
def has_password?(submitted_password)
# Compare encrypted_password with the encrypted version of submitted_password
encrypted_password == encrypt(submitted_password)
end
# Static/Class methods
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 functionality.
# Anything after the 'private' pragma will be inaccessable from outside the class
private
def encrypt_password
self.salt = make_salt if new_record? # Using ActiveRecord goodness to make sure this only gets created once.
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
end
routes.rb
SampleApp::Application.routes.draw do
#get '/users/new'
resources :users
match '/signup' => 'users#new'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
match '/help' => 'pages#help'
root :to => 'pages#home'
end
Thanks in advance. If you guys really want to dig through or if I've missed something in my post, here's my code.
I'm very new to rails, and absolutely love it so far. Any help will be greatly appreciated.
Look closely at your users_controller_spec "success" spec: when will it create #attr? Before each test, or just before the "should create a new user" test? You use it in all the "POST 'create'" tests...
Once you make that non-spec-specific your tests will pass.
(By the way, having the code up in git is handy, but only if the code you're posting is actually checked in, otherwise... not as much ;)
your
it "should redirect to the user show page"
and
it "should have a welcome message"
is outside of the
describe "success" do
loop
I am using Ruby on Rails 3.0.10, RSpec 2 and FactoryGirl. I have the following scenario:
In the models/user_spec.rb file I have
describe User do
let(:user) { Factory(:user) }
it "should have a 'registered' authorization do
user.authorization.should == "registered"
end
end
In the factories/user.rb file I have
FactoryGirl.define do
factory :user, :class => User do |user|
user.authorization 'registered'
end
end
In the user.rb file I have:
class User < ActiveRecord::Base
DEFAULT_AUTHORIZATION = 'registered'
validates :authorization,
:inclusion => {
:in => Authorization.all.map(&:name),
:message => "authorization is not allowed"
},
:presence => true
before_validation :fill_user_create, :on => :create
private
def fill_user_create
self.authorization = Authorization::DEFAULT_AUTHORIZATION
end
end
When I run the rspec command I get the following error:
User should have a default 'registered' Authorization
Failure/Error: let(:user) { Factory(:user) }
ActiveRecord::RecordInvalid:
Validation failed: Users authorization is not allowed
What is exactly the problem and how can I solve that?
BTW: In the models/user_spec.rb file I can use something like the following
let(:user) { User.create }
and it will work, but I prefer to use the FactoryGirl gem. What do you advice about?
Could you try modifying your spec as below and check what the results are:
it "should have a 'registered' authorization" do
system_names = Authorization.all.map(&:system_name)
system_names.should have_at_least(1).item
system_names.should include('registered')
user.authorization.should == "registered"
end