It's not some kind of synchronization problem I readed before.
The code is quite simple.
The model:
class User < ActiveRecord::Base
attr_accessor :name, :email
validates_uniqueness_of :email, :on => :create, :message => "must be unique"
end
The rspec test:
require 'spec_helper'
describe User do
before(:each) do
#valid_attributes = {
:name => "Foo Bar",
:email => "foo#bar.com"
}
end
it "should reject duplcate email address" do
User.create!(#valid_attributes)
duplicate_user = User.new(#valid_attributes)
duplicate_user.should_not be_valid
end
end
I run the test, and get error message:
----------------------------
1)
'User should reject duplcate email address' FAILED
expected #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> not to be valid
/Users/mac/workspace/rails_space/uniq/spec/models/user_spec.rb:14:
Finished in 0.067908 seconds
1 example, 1 failure
-----------------------------
I run the script/console, and create two user objects with same email address. It goes fine, no validate message occur, the two objects both have inserted into the table. I don't what's wrong with it.
My rails version is 2.3.8 and rspc is 1.3.0.
I believe the problem is the attr_accessor line that you have. If you have those column names, the accessor will override the column name and that is just part of the class and doesn't care about uniqueness. If you are going to have the accessor methods then it needs to get back to the database in some way. If you need to have the accessor, then you need to tie it to the database by calling write_attribute.
For more information you can see the documentation for "Overwriting default accessors" at http://api.rubyonrails.org/classes/ActiveRecord/Base.html
I hope this helps!
I think the issue is because you are saying:
validates_uniqueness_of :email, :on => :create
User.new may not be triggering this validation.
Try calling duplicate_user.save! and see if that throws an error.
You can try like following
attr_accessible :email
validates_uniqueness_of :email, :on => :create, :message => "must be unique"
Related
So I have been racking my brain at this and maybe some of you might have a better idea on how to do proper unit test for this User model. My basic unit test looks like this.
test "should not save without name" do
user = User.new
user.email = "test#test.com"
user.password = "letmein"
assert !user.save
end
This test passes with this model.
class User < ActiveRecord::Base
include Clearance::User
validates :name, presence: true
has_and_belongs_to_many :contests
end
Is there a better way to do this in Clearance? It is nice the gem lets you create users like this on the fly by arbitrarily assigning email and password but I'm thinking maybe I shouldn't have to do this.
user = User.new(:email => "test#test.com", :password => "letmein")
and then,
assert !user.valid?
or
user.should_not be_valid
or
expect { user.save }.to change(User, :count).by(0)
I have a user model:
class User < ActiveRecord::Base
attr_accessible :email, :name
# admin only
attr_accessible :email, :name, :admin, :as => :admin
And the following model spec:
describe "accessible attributes" do
let(:new_user){ FactoryGirl.create(:user) }
#admin_attrs = { admin: true, name: "ben", email: "xyz#test.com"}
it "can not be set on create" do
# variant 1 - test fails, AM::MAS::Error NOT thrown
expect do
User.new(#admin_attrs)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
# variant 2 - test passes, AM::MAS::Error NOT thrown
expect do
User.new(admin: true, name: "ben", email: "xyz#test.com", password: "123xyz", password_confirmation: "123xyz")
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
I can't figure out why variant 1 of my test fails, but variants 2 passes. The error message I get is:
1) User accessible attributes can not be set on create
Failure/Error: expect do
expected ActiveModel::MassAssignmentSecurity::Error but nothing was raised
They are basically the same test. What am I doing wrong? In fact, if I perform test 1 from the console, it does throw a MAS::Error, as expected. I'm confused.
#admin_attrs is probably nil as far as your first text example is concerned because it's not being set up in a before hook (or through 'let')
I have the following code in a micropost_spec.rb:
before(:each) do
#user = Factory :user
#attr = { :paragraphs => 4, :characters => 1000, :summary => "Recap of Event" }
end
it "should create a new instance given valid attributes" do
#user.microposts.create!(#attr)
end
And when I run my tests, I get the following error:
Failure/Error: #user.microposts.create!(#attr)
ActiveRecord::RecordInvalid:
Validation failed: Id can't be blank
Is it possible to see if the id is set inside the rails console?
Your model is probably trying to validate that id is present with something like validates_presence_of :id or validates :id, :presence => true. You shouldn't do that, as validations on new records are always run before the record is saved to the database and assigned an id.
I set up a User AR model which has conditional validation which is pretty much identical to the Railscast episode on conditional validation. So basically my User model looks like this:
class User < ActiveRecord::Base
attr_accessor :password, :updating_password
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 },
:if => :should_validate_password?
def should_validate_password?
updating_password || new_record?
end
end
Now in my action where the User can change their password I have the following two lines:
#user.updating_password = true
if #user.update_attributes(params[:user]) ...
so that I flag the validations to be run on the password. In development mode this works great - if the user tries to put in a password that is too short or too long the model does not pass validation. My problem is that for the life of me I can not get my tests for this to pass. Here is my spec:
require 'spec_helper'
describe PasswordsController do
render_views
before(:each) do
#user = Factory(:user)
end
describe "PUT 'update'" do
describe "validations" do
before(:each) do
test_sign_in(#user)
end
it "should reject short passwords" do
short = "short"
old_password = #user.password
#attr2 = { :password => short, :password_confirmation => short }
put :update, :user_id => #user, :old_password => #user.password, :user => #attr2
#user.password.should == old_password
end
it "should reject long passwords" do
long = "a" * 41
old_password = #user.password
#attr2 = { :password => long, :password_confirmation => long }
put :update, :user_id => #user, :old_password => #user.password, :user => #attr2
#user.password.should == old_password
end
end
end
end
When I run these tests I always get the error:
1) PasswordsController PUT 'update' validations should reject short passwords
Failure/Error: #user.password.should == old_password2
expected: "foobar"
got: "short" (using ==)
and of course the error for the password being too long. But should'nt the password be validated as a result of me setting #user.updating_password = true before any save attempts in the controller?
I think the problem isn't the code but what you expect it to do. When you call update_attributes and pass in a bad value, the value is saved into the model object even though the validation fails; the bad value has not been pushed to the database.
I think this makes sense because when the validation fails normally you would show the form again with the error messages and the inputs populated with the bad values that were passed in. In a Rails app, those values usually come from the model object in question. If the bad values weren't saved to the model they would be lost and your form would indicate that the old 'good' values had failed validation.
Instead of performing this check:
#user.password.should == old_password
Maybe try:
#user.errors[:password].should_not == nil
or some other test that makes sense.
i've been following the Rails tutorial (http://railstutorial.org/chapters/beginning , Rails 3 version), and i've stopped at 11th chapter when using Factory Girl and Rspec, I have a test that isn't passing and I feel I'm doing something wrong but I don't see what.
First of all there is a git repository on Github with the code that doesn't pass that test.
http://github.com/Monomachus/ch3_static_pages
So I got users model
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
has_many :microposts
.
.
.
I got microposts model
class Micropost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
default_scope :order => 'microposts.created_at DESC'
end
Then I got Factory girl settings
Factory.define :user do |user|
user.name "Michael Hartl"
user.email "mhartl#example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.define :micropost do |micropost|
micropost.content "Foo bar"
micropost.association :user
end
And finally Rspec code
require 'spec_helper'
describe Micropost do
.
.
describe "microposts associations" do
before(:each) do
#user = User.create(#attr)
#mp1 = Factory(:micropost, :user => #user, :created_at => 1.day.ago)
#mp2 = Factory(:micropost, :user => #user, :created_at => 1.hour.ago)
end
it "should have a microposts attribute" do
#user.should respond_to(:microposts)
end
it "should be in the reverse order of appearing" do
#user.microposts.should == [#mp2, #mp1]
end
end
end
And I got the error which definitely tells me that I do something wrong.
Failures:
1) Micropost microposts associations should be in the reverse order of appearing
Failure/Error: #user.microposts.should == [#mp2, #mp1]
expected: [#<Micropost id: 2, content: "Foo bar", user_id: nil, created_at: "2010-12-24 12:47:02", update
d_at: "2010-12-24 13:47:02">, #<Micropost id: 1, content: "Foo bar", user_id: nil, created_at: "2010-12-23 13:
47:02", updated_at: "2010-12-24 13:47:02">],
got: [] (using ==)
Diff:
## -1,3 +1,2 ##
-[#<Micropost id: 2, content: "Foo bar", user_id: nil, created_at: "2010-12-24 12:47:02", updated_at: "20
10-12-24 13:47:02">,
- #<Micropost id: 1, content: "Foo bar", user_id: nil, created_at: "2010-12-23 13:47:02", updated_at: "20
10-12-24 13:47:02">]
+[]
# ./spec/models/micropost_spec.rb:42:in `block (3 levels) in <top (required)>'
As you can see even the user_id property is not set correctly +
apparently #user.microposts doesn't have any elements.
Please help me with this issue thanks.
Well the answer was simple :)
I included microposts associations in the Micropost spec.
And clearly
describe "microposts associations" do
before(:each) do
#user = User.create(#attr)
#mp1 = Factory(:micropost, :user => #user, :created_at => 1.day.ago)
#mp2 = Factory(:micropost, :user => #user, :created_at => 1.hour.ago)
end
it "should have a microposts attribute" do
#user.should respond_to(:microposts)
end
it "should be in the reverse order of appearing" do
#user.microposts.should == [#mp2, #mp1]
end
end
#attr did not contain the user properties but the micropost properties and of course #user = nil and then everything makes sense. So if you do have the same problem, include this code into User spec.
Now all my tests pass :)
By the time I had finished the pagination chapter, the tutorial was creating 100 sample users using Faker (listing 10.25 on page 390), and in RubyMine I was able to see my test was failing because the program was throwing an exception on duplicate user email address (which has a unique constraint). The #attr on line 8 of user_spec.rb has :email => "user#example.com", however this throws an exception since it's a duplicate email (I guess because Faker has already created it).
For me the fix was to copy #attr from line 8 and paste it into the describe "micropost associations" block (user_spec.rb), and change the email address to :email => "user#example999.com". I'm sure this is a total hack but I'm a n00b.
Update:
Another fix for me was to comment out the line #user = User.create(#attr), and simply create #mp1 and #mp2.
I was also getting test failure in this section, even though I already had "micropost associations" in user_spec.rb. Turns out I needed to restart spork and autotest in order to get them to use the new "micropost" factory in factories.rb.