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.
Related
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
I am using FactoryGirl and populate unique attribute whenever the model is made. The problem with my model form is that there are only 4 different types available for form_type attribute. So I need to reset the sequence everytime I run tests. Like below, I user before do block to call FactoryGirl.reload. However, I saw an article saying it is anti-pattern to FactoryGirl. What is the best way to reset the sequence in FactoryGirl instead of calling FactoryGirl.reload before every test?
Here is my forms.rb Factorygirl file,
FactoryGirl.define do
factory :form do
association :user
sequence :form_type do |n|
Form.form_types.values[n]
end
end
end
Here is my form.rb model file:
class Form < ActiveRecord::Base
belongs_to :user, required: true
enum form_types: { :a => "Form A", :b => "Form B", :c => "Form C", :d => "Form D"}
validates :form_type, presence: true
validates :form_type, uniqueness: {scope: :user_id}
end
Here is my forms_controller_spec.rb file:
require 'rails_helper'
RSpec.describe FormsController, type: :controller do
login_user
let(:form) {
FactoryGirl.create(:form, user: #current_user)
}
let(:forms) {
FactoryGirl.create_list(:form , 3, user: #current_user)
}
let(:form_attributes) {
FactoryGirl.attributes_for(:form, user: #current_user)
}
describe "GET #index" do
before do
FactoryGirl.reload
end
it "loads all of the forms into #forms" do
get :index
expect(assigns(:forms)).to match_array(#forms)
end
end
end
Hm, it seems like the purpose of FG sequences is to ensure unique numbers. Or at least that's what I've used it for. You may be able to hack into FG if this is what you really want.
This question may help.
How can I reset a factory_girl sequence?
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
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.
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"