I am trying to Test the User Model Spec for User Creation
factories/users.rb
FactoryGirl.define do
factory :user do
first_name {Faker::Name.first_name}
last_name {Faker::Name.last_name}
email {Faker::Internet.email}
username {Faker::Internet.user_name}
password {Faker::Internet.password}
end
end
specs/models/user_spec.rb
require 'rails_helper'
describe User, :type => :model do
context "valid Factory" do
it "has a valid factory" do
expect(build(:user)).to be_valid
end
end
context "validations" do
before { create(:user) }
context "presence" do
it { should validate_presence_of :first_name }
it { should validate_presence_of :last_name }
it { should validate_presence_of :email }
it { should validate_presence_of :encrypted_password }
end
context "uniqueness" do
it { should validate_uniqueness_of :email }
it { should validate_uniqueness_of :username }
end
end
end
I am using Devise for the USer creation. But i am ending up with the following Test Failure
User
valid Factory
has a valid factory
validations
presence
should require first_name to be set
should require last_name to be set
should require email to be set
should require encrypted_password to be set (FAILED - 1)
uniqueness
should require case sensitive unique value for email
should require case sensitive unique value for username
Failures:
1) User validations presence should require encrypted_password to be set
Failure/Error: it { should validate_presence_of :encrypted_password }
Expected errors to include "can't be blank" when encrypted_password is set to nil,
got no errors
# ./spec/models/user_spec.rb:18:in `block (4 levels) in <top (required)>'
Finished in 0.98827 seconds (files took 6.61 seconds to load)
7 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:18 # User validations presence should require encrypted_password to be set
Am assuming that the encrypted_password will be auto generated by Devise on trying to create the user.
Devise does not actually validate the encrypted password since it is created dynamically after validation if the password is "dirty" (changed).
You don't actually need to test the encrypted password in that way since it is an implementation detail. You can test that Database authenticable is working properly by doing something like:
it 'is database authenticable' do
user = User.create(
email: 'test#example.com',
password: 'password123',
password_confirmation: 'password123'
)
expect(user.valid_password?('password123')).to be_truthy
end
But the actual value of the test is pretty low. Instead with Devise you you may want to focus on some end-to-end tests (feature specs) where you test the user path of signing up and then logging in.
I would suggest you only test those validations you added, you do not need to test validations added by devise since they are already been tested.
Related
I am learning testing with RSpec. Something is not working with my tests.
My model:
class User < ActiveRecord::Base
has_secure_password
# Validation macros
validates_presence_of :name, :email
validates_uniqueness_of :email, case_sensitive: false
end
My factory:
FactoryGirl.define do
factory :user do
name "Joe Doe"
email "joe#example.com"
password_digest "super_secret_password"
end
end
And my spec:
require 'rails_helper'
RSpec.describe User, type: :model do
user = FactoryGirl.build(:user)
it 'has a valid factory' do
expect(FactoryGirl.build(:user)).to be_valid
end
it { is_expected.to respond_to(:name) }
it { is_expected.to respond_to(:email) }
it { is_expected.to respond_to(:password) }
it { is_expected.to respond_to(:password_confirmation) }
it { expect(user).to validate_presence_of(:name) }
it { expect(user).to validate_presence_of(:email) }
it { expect(user).to validate_presence_of(:password) }
it { expect(user).to validate_uniqueness_of(:email).case_insensitive }
end
I expected this test to pass. But I get this as a result:
Failures:
1) User should validate that :email is case-insensitively unique
Failure/Error: it { expect(user).to validate_uniqueness_of(:email).case_insensitive }
User did not properly validate that :email is case-insensitively unique.
The record you provided could not be created, as it failed with the
following validation errors:
* name: ["can't be blank"]
# ./spec/models/user_spec.rb:18:in `block (2 levels) in <top (required)>'
Finished in 0.34066 seconds (files took 1.56 seconds to load) 9
examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:18 # User should validate that :email
is case-insensitively unique
What I am missing?
Update
I think that this is a bug: https://github.com/thoughtbot/shoulda-matchers/issues/830
It is because you are declaring it 2 times IMO! First building user then building same user inside expect().
Just use ur first user that you have built with factory-bot like so:
it 'has a valid factory' do
expect(user).to be_valid
end
P.S
It is better to use Faker gem instead of using harcoded instances like you did in factory.rb
Your Variable Is Currently Only Set Once for All Tests
When you write code like:
RSpec.describe User, type: :model do
user = FactoryGirl.build(:user)
end
you aren't building a new user each time you run a new spec. Likewise, using #let is the wrong approach, because it memoizes the variable even between tests. Instead, you need a to use an RSpec before#each block. For example:
describe User do
before do
#user = FactoryGirl.build :user
end
# some specs
end
If you have tests which are persisting you user to the database, and if you have disabled rollback or database cleaning between tests, then your defined factory (as currently written) will certainly fail the uniqueness validation. In such cases, you may want to try:
User.delete_all in your test, or otherwise cleaning your database between tests.
Using FactoryGirl sequences or the Faker gem to ensure that user attributes are actually unique.
USE let
RSpec.describe User, type: :model do
let(:user) { FactoryGirl.build(:user) }
# other what you need
I am testing my account model with shoulda-matchers using a entity created with FactoryGirl.
The code of both files look like this:
TEST FILE
require 'spec_helper'
describe Account do
before { #account = FactoryGirl.build(:account) }
subject { #account }
it { should validate_presence_of(:email) }
it { should validate_presence_of(:password) }
it { should validate_presence_of(:password_confirmation).
with_message(#account.password_confirmation+' '+#account.password) }
it { should allow_value('example#domain.com').for(:email) }
it { should be_valid }
end
FACTORY
FactoryGirl.define do
factory :account do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
My error is the following:
1) Account should require password_confirmation to be set
Failure/Error: it { should validate_presence_of(:password_confirmation).
Expected errors to include "12345678 12345678" when password_confirmation is set to nil, got errors: ["password_confirmation can't be blank (nil)"]
# ./spec/models/account_spec.rb:9:in `block (2 levels) in <top (required)>'
rspec ./spec/models/account_spec.rb:9 # Account should require password_confirmation to be set
I am using devise which should check for password confirmation. I know its probably because of something stupid, but I really can't figure out what's wrong with it.
Devise, doesn't validate the presence of password_confirmation it just validates the confirmation of password see Validatable line 34: https://github.com/plataformatec/devise/blob/v3.5.3/lib/devise/models/validatable.rb#L34
validates_confirmation_of :password, if: :password_required?
EDIT: You could also see that there are no validators on password_confirmation running: User.validators_on(:password_confirmation)
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)
I've been trying to get a grasp on writing tests, but having a lot of trouble as the tests never seem to validate the way I want them to. In particular, I've been trying to use Factory Girl as opposed to fixtures - as suggested by a recent Railscasts and other advice I've seen on the net - due to the benefits it professes, and that hasn't worked out.
For example, here is a simple Rspec test for a user model, testing to make sure a username is present...
describe User do
it "should not be valid without a username" do
user = FactoryGirl.create(:user, :username => "", :password => "secret")
user.should_not be_valid
end
end
And my factories.rb file, if it helps...
FactoryGirl.define do
factory :user do
sequence(:username) { |n| "registered-#{n}" }
password "foobar"
end
end
When I run 'rake spec,' it tells me...
1) User should not be valid without a username
Failure/Error: user = FactoryGirl.create(:user, :username => "", :password => "secret")
ActiveRecord::RecordInvalid:
Validation failed: Username can't be blank
Ummm...that's the POINT. If I specified that the user should NOT be valid, shouldn't this test actually pass?
If I replace the Factory Girl line and set the user in the test with something like 'user = User.new(:username => "", :password => "secret")', to no surprise the test passes fine. So why is Factory Girl not working right?
You should use build like in the following:
user = Factory.build(:user, :username=>"foo")
Because using the method you're using will try to create a record. See docs for further information.
I have a User model and Authentications model, which is a basic omniauth setup. Essentially, users can sign up through oauth without setting a password.
I have a Authentication.is_destroyable? method that returns true if the user has a password or has more than one authentication. Essentially, this prevents users deleting their one and only way of authentication.
def is_destroyable?
if user.encrypted_password.present? || user.authentications.count > 1
true
else
errors.add :base, 'not allowed'
false
end
end
When testing this in development it works as expected under all conditions. However, my unit tests are failing:
describe "Authentication#is_destroyable?" do
before(:each) do
# This creates a user with no password and a single authentication
#user = FactoryGirl.create(:user_with_oauth)
#auth = #user.authentications.first
end
# This spec passes :)
it "should return false when is users only authentication method" do
#auth.is_destroyable?.should be_false
end
# This FAILS - I have no idea why :(
it "should return true when user has multiple authentications" do
#user.authentications.create FactoryGirl.attributes_for(:authentication, :provider => 'twitter')
#auth.is_destroyable?.should be_true
end
# This FAILS - I have no idea why :(
it "should return true when user has a password" do
#user.update_attributes :password => 'password'
#auth.is_destroyable?.should be_true
end
end
I've spent the best part of 3 hours banging my head against the wall. I can't for the life of me understand why this works when I manually test the functionality (and Cucumber stories pass also testing the functionality), but in rspec the unit tests are failing. Is there something obvious that I'm missing?
Edit
As requested, here's some further detail.
Both failing specs fail with:
Failure/Error: #auth.is_destroyable?.should be_true
expected false to be true
The Factories:
FactoryGirl.define do
factory :user do
username { FactoryGirl.generate(:username) }
name 'Test User'
email { FactoryGirl.generate(:email) }
password 'password'
end
factory :user_with_oauth, :parent => :user do
password nil
authentications [ FactoryGirl.build(:authentication) ]
end
factory :authentication do
provider 'facebook'
uid SecureRandom.hex(16)
end
end
Also, maybe relevant, am using DatabaseCleaner with the truncation strategy.
I can answer my own question (after 2 more hours of hitting my head against the wall)...
My :user_with_oath Factory was to blame; I wasn't wrapping the authentications association in a block:
factory :user_with_oauth, :parent => :user do
password nil
authentications { [FactoryGirl.build(:authentication)] }
end