Trying to figure out why my rspec test is failing. Most notable is the Failure message that seems contradictory. Stating I have an ActiveRecord::RecordInvalid error and that is exactly what I'm asserting should happen.
Here is my user.rb
...
validates_presence_of :email
...
Here is my users_spec.rb
...
it "is invalid without email" do
Factory(:user, email: nil).should raise_error(ActiveRecord::RecordInvalid)
end
...
here is the output:
Failures:
1) User a user (in general) is invalid without email
Failure/Error: Factory(:user, email: nil).should raise_error(ActiveRecord::RecordInvalid)
ActiveRecord::RecordInvalid:
Validation failed: Email is invalid, Email can't be blank
# ./spec/models/user_spec.rb:34:in `block (3 levels) in <top (required)>'
Originally I was testing it this way but it kept failing, so I decided to specify on what error I was expecting.
it "is invalid without email" do
Factory(:user, email: nil).should_not be_valid
end
The reason your code isn't working is that you're trying to create an invalid model before actually testing it for validity. What you want to do is to create a valid model, change something and check that it is invalid, like this:
it "is invalid without email" do
user = Factory(:user)
user.email = nil
user.should_not be_valid
end
I personally like to define my model in a before block, set is as the subject and then change attributes in each spec and check for validity, like this:
before do
#user = FactoryGirl.create(:user)
end
subject { #user }
it "is invalid without email" do
subject.email = nil
should_not be_valid
end
For the record, if you wanted to test that the record creation raised an error (which is definitely not the advisable way to do this), you could do it by wrapping the Factory call in a lambda, like this:
lambda {
Factory(:user, :email => nil)
}.should raise_error(ActiveRecord::RecordInvalid)
Related
I have this spec that I want to translate to MiniTest.
describe User do
subject { build(:user, provider: 'foo') }
# don't validate presence of password when provider is present
it do
should_not validate_presence_of(:password)
end
end
I tried this. I am getting an error of undefined method 'should_not' for UserTest
class UserTest < ActiveSupport::TestCase
def setup
#user = build_stubbed(:user)
end
test "responds to name" do
assert_respond_to #user, :name
end
should validate_presence_of(:password)
test "do not validate presence of password when provider is present" do
build_stubbed(:user, provider: 'foo')
should_not validate_presence_of(:password)
end
end
I want to change the context for one test, where the subject gets a provider attribute, which should disable the presence validator on the password field.
Here's the full error:
UserTest#test_presence_of_password:
NoMethodError: undefined method `should_not' for #<UserTest:0x007feaa82c1c68>
test/models/user_test.rb:45:in `block in <class:UserTest>'
I found that the better way to do this is to revert to good old MiniTest:
test "uniqueness of email with a different provider" do
email_user = create(:user, email: "foo#bar.com")
facebook_user = build_stubbed(:facebook_user, email: "foo#bar.com")
assert facebook_user.valid?, "should be valid with same email if provider is different"
end
Take a look at the minitest-rails-shoulda gem. If you use it I assume the test would look like this:
describe User do
subject { build_stubbed(:user) }
it { must validate_presence_of(:password) }
describe "when a provider is present" do
subject { build_stubbed(:user, provider: 'foo') }
it { wont validate_presence_of(:password) }
end
end
Here is my Spec file:
require 'spec_helper'
describe User, "references" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
end
describe User, "factory" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "is invalid with no email" do
#user.email = nil
#user.should_not be_valid
end
it "is valid with email" do
#user.should be_valid
end
end
Factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
end
end
The part I am trying to "test" for and not sure how to 100% is checking to make sure when a User is created that the email address is not nil.
shoulda provides validation helpers to help you test the validations.
it { should validate_presence_of(:email) }
If you want to use rspec and write your own, then
describe User do
it "should be invalid without email" do
user = FactoryGirl.build(:user, :email => nil)
#user.should_not be_valid
#user.errors.on(:email).should == 'can't be blank' #not sure about the exact message. But you will know when you run the test
end
it "should be valid with email" do
user = FactoryGirl.build(:user, :email => "user#user.com")
#user.should be_valid
end
end
When you run the test, it would read as
User
should be invalid without email
should be valid with email
Giving a good description for your test case is very important, because it kind of acts like a documentation.
I'm testing that email is not required on the user model on update.
With FactoryGirl:
u = FactoryGirl.create(:user)
u.email = nil
expect(u.save).to be_true
The test passes.
With shoulda:
should_not validate_presence_of(:email).on(:update)
The test fails with error:
Failure/Error: should_not validate_presence_of(:email)
Did not expect errors to include "can't be blank" when email is set to nil, got error: can't be blank
Anyone have any thoughts on why this discrepancy occurs?
I think this is what's happening.
The should_not test is using an implicit subject of User.new. In order to test update on this subject, it must first be saved, but saving will result in an error because of the validation on create. The workaround is create/save a valid user object and to call should_not validate_presence_of(...).on(:update) explicitly on that object.
See the related Shoulda: Test validates_presence_of :on => :update
I'm testing my User model and studying how FactoryGirl works. When I do this in my user_spec.rb:
before(:each) do
#user = User.new(username: 'ExampleUser', email: 'user#example.com', timezone: 'Eastern Time (US & Canada)', password: 'example')
end
Everything passes, but if I do:
before(:each) do
#user = FactoryGirl.create(:user)
end
It fails the test to see if the user's username and email are taken already.
1) User when username is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/user_spec.rb:151:in `block (3 levels) in <top (required)>'
2) User when email address is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/user_spec.rb:142:in `block (3 levels) in <top (required)>'
Finished in 1.8 seconds
29 examples, 2 failures
These are the test:
describe 'when email address is already taken' do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
describe 'when username is already taken' do
before do
user_with_same_username = #user.dup
user_with_same_username.username = #user.username.upcase
user_with_same_username.save
end
it { should_not be_valid }
end
Can someone explain? I thought FactoryGirl was suppose to let me use it like User.new, my first example which works.
FactoryGirl.create actually creates the record, whereas User.new only instantiates the model but does not actually save the record.
If you want to only instantiate the model, you should use FactoryGirl.build:
before(:each) do
#user = FactoryGirl.build(:user)
end
See the documentation for details.
So what I think is happening with your current code is that when you create the user with FactoryGirl.create, it actually saves the record with no validation issues (since the duplicate has not been created yet). When you save the user with the same email with user_with_same_email.save, it does not actually save that user but you don't see that. Then when you check if the original user is valid, it says yes because you already saved it before trying (and failing) to create the duplicate.
Make sense? Anyway just switch to FactoryGirl.build and both tests should pass.
Generally when testing a field, using Factory Girl, that has validates_uniqueness_of it's best to use a sequence.
When using a sequence, every time you create a record with FactoryGirl.create(:user), the username will always be unique. This allows you to work with "real" records in your database without having to manually correct for conflicting values.
factory :user do
sequence :username do |n}
"user_#{n}"
end
end
Note: I don't like the idea of testing records that haven't been added to the database. I can't think of any solid reason why it would be a problem. The only problem I can think of is the fact that you won't be able to test associations.
Something else that I keep noticing with your questions is you use a before block and create instance variables. In RSpec there is a method called let will create the variable when it is needed.
This would make your user_spec.rb file work like so.
describe User do
let(:user) { create(:user, :first_name => "John", :last_name => "Doe") }
it "should get full name" do
user.full_name.should == "John Doe"
end
end
let also has a bang method that will create the variable whether it is used in the it block or not.
I am testing Devise with Rspec using Micheal Hartl source code (railstutorial)
Whereas the confirmable module is enabled, I don't understand why this test pass:
spec/models/user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
#attr = { :username => "ExampleUser",
:email => "user#example.com",
:password => 'test1234',
}
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
end
Basically, I want to be sure of this code does, it tests the creation on the user, not this validation (cause the user has not confirmed yet and the test returns true) ? This is right?
Moreover, I didn't provide attribute for password confirmation, and the user is still created!
Is this mean that in the :validatable module there is not (?):
validates :password, :confirmation => true
Thanks to get you view on this!
one problem is the trailing comma at the end of your each block. second, you are not asserting anything in your test to pass or fail the test, though you are probably erroring out at this point, which is why you are saying it didnt pass.
you can try assigning the user object to a variable:
it "should create a new instance given valid attributes" do
#user = User.new(#attr)
#user.should be_valid #=> new will let you know if its valid or not
#user.save.should be_true #=> another possible assertion to pass/fail the test
# debug message to give you back why it failed
puts #user.errors.full_messages.to_sentence
end