I'm trying to get my valid password test to pass. When I run it it seems that the password_digest hash is different. I don't know what to do to get them to match.
I was mostly using the book "Ruby on Rails Tutorial: Learn Rails by Example" by Michael Hartl and it seems that he gets this to pass.
Additionally, my application code works just as expected. I can create a user and authenticate them in the console so this is only breaking in tests.
I'm fairly new to testing so I may be missing something obvious here.
Thanks for your help!
I am using bcrypt with has_secure_password and here's part relavent user spec code:
describe User do
before { #user = User.new(name: "Example User", email: "user#example.com", password: "foobar", password_confirmation: "foobar") }
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:authenticate) }
it { should be_valid }
describe "return value of authenticate method" do
before { #user.save }
let(:found_user) { User.find_by(email: #user.email) }
describe "with a valid password" do
it { should eq found_user.authenticate(#user.password) }
end
describe "with an invalid password" do
let(:user_for_invalid_password) { found_user.authenticate('invalid') }
it { should_not eq user_for_invalid_password }
specify { expect(user_for_invalid_password).to be_false }
end
end
end
And this is my user model
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :email, presence: true, uniqueness: { case_sensitive: false }, format: { with: VALID_EMAIL_REGEX }
has_secure_password
validates :password_confirmation, presence: true
validates :password, length: { minimum: 6 }
end
The problem is in your before { #user.save }. You may not have realized it, but before defaults to before(:each), which means this code runs before each example in the group. You probably expected it to default to before(:all), which runs only once, before all examples in the group.
As you mentioned, you aren't clearing the database after each test. Therefore, before each test, you are calling #user.save. This silently fails validation by returning false, because you are trying to create another user with the same email address. It's better to use #save! so validation exceptions are thrown, making problems like this more obvious.
To summarize:
Explicitly use before(:all) or before(:each)
Use #save! instead of #save
Clear the database between each test (try out Database Cleaner)
Related
I have been creating an application based on the Hartl course, and have added in the concept of Organization, which has_many users. The tests are all the standard tests Hartl recommends up to section 9.2 of the guide book. Since implementing Organizations into the application, one of the test cases is failing "when email address is already taken" - this should block a user from signing up with the same email address twice. What is odd is the fact that this is working in the application itself (form error - "users email address is already taken" thrown) but not in my tests. Can you help and indicate why this has broken please?
User code:
class User < ActiveRecord::Base
belongs_to :organization
#accepts_nested_attributes_for :organization
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
validates :organization, presence: true
Organization code:
class Organization < ActiveRecord::Base
validates :organization_name, presence: true, length: { maximum: 50 }, uniqueness: true
has_many :users, :inverse_of => :organization
accepts_nested_attributes_for :users
User spec:
require 'spec_helper'
describe User do
before do
#user = FactoryGirl.create(:user)
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:remember_token) }
it { should respond_to(:authenticate) }
it { should be_valid }
...
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
Factory Girl Code:
FactoryGirl.define do
factory :organization do
organization_name "Example Org"
trait :wrong do
organization_name "Wrong Org"
end
trait :also_wrong do
organization_name "Another Wrong Org"
end
end
factory :user do
association :organization
name "Example Name"
email "email#example.com"
password "foobar"
password_confirmation "foobar"
trait :wrong_org do
association :organization, :factory => [:organization, :wrong]
end
trait :wrong_org2 do
association :organization, :factory => [:organization, :also_wrong]
end
end
end
The error thrown from the Rails console is as follows:
1) User when email address is already taken should not be valid
Failure/Error: it { should_not be_valid }
expected #<User id: 5287, name: "Example Name", email: "email#example.com", created_at: "2014-07-22 15:04:33", updated_at: "2014-07-22 15:04:33", password_digest: "$2a$04$jrxyuz9e574BoaAhZm6xkOUeAY5spyDut2CCEvAykMu...", organization_id: 5025, remember_token: "339dfafcac7bc5925dbf4e44f60a782f3bbbaa1b">.valid? to return false, got true
I've tried changing the code inside the test, but no matter what I do it keeps throwing an error. As mentioned above, when I open up the application in my local server I can use all functions, and when I try to sign up using a duplicate email address it won't let me. What's wrong with my test code?
The #user is completely valid:
You create your subject, #user. This is valid
You create the user_with_same_email
That user is not valid, because it has the same email as #user
Saving user_with_same_email returns false, but that is not checked in your test
The duplicate user is not saved in the db
The original user is still valid
A correct test would just #dupthe user (or make a new one with the same email), and then check that the new record is not valid.
I'm following the Rails Tutorial Modeling Users Chapter: http://www.railstutorial.org/book/modeling_users#cha-modeling_users.
My user.rb looks like:
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
has_secure_password
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
end
and my user model spec looks like:
describe User, :type => :model do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:authenticate) }
it { should be_valid }
... (other methods are here)
describe "when password is not present" do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
it { should_not be_valid }
end
describe "return value of authenticate method" do
before { #user.save }
let(:found_user) { User.find_by(email: #user.email) }
describe "with valid password" do
it { should eq found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not eq user_for_invalid_password }
specify { expect(user_for_invalid_password).to be_false }
end
end
Which I'm pretty sure is exactly a duplication what the Rails Tutorial code is, but I'm getting the following failed test errors:
rspec ./spec/models/user_spec.rb:83 # User when password is not present should not be valid
rspec ./spec/models/user_spec.rb:108 # User return value of authenticate method with invalid password should be false
I checked this out by looking at the Ruby on Rails Tutorial Book's source code (Rails 4) at GitHub: spec/models/user_spec.rb. Based on the code there, it looks like your passwords are currently of acceptable type and that's why your test is failing. I mean your passwords are good. foobar is a valid password. Below an empty string is passed to the User model validation.
describe "when password is not present" do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: " ", password_confirmation: " ")
end
it { should_not be_valid }
end
The second one I'm not sure, but would it help if you tried the Rails 3 spec/models/user_spec.rbrelated code for the same test:
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
It looks slightly different but it's testing the same thing. This is just suggestion, because I'm not sure what's going wrong.
It looks like a couple of methods are missing (if you're trying to match the tutorial exactly) Listing 6.25 & 6.28:
describe "when password doesn't match confirmation" do
before { #user.password_confirmation = "mismatch" }
it { should_not be_valid }
end
describe "with a password that's too short" do
before { #user.password = #user.password_confirmation = "a" * 5 }
it { should be_invalid }
end
In chapter 6.x of the Ruby on Rails tutorial by Michael Hartl, I can't get Rspec to pass the email is not present check.
From my limited knowledge:
the User_spec creates a test user using the code after before do.
This user is then checked against the attributes in user.rb to make
sure the :presence is valid.
The check then returns true or false if its valid.
In the next code, before { #user.email = " " } sets the email to
empty
then says it { should_not be_valid }
However, it fails User when email is not present error.
/spec/models/User_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should be_valid }
describe "when email is not present" do
before { #user.email = " " }
it { should_not be_valid }
end
end
spec/models/user.rb
class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true
end
Failures:
1) User when email is not present
Failure/Error: it { should_not be_valid }
expected # not to be valid
# ./spec/models/user_spec.rb:18:in `block (3 levels) in '
Finished in 0.03278 seconds
4 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:18 # User when email is not present
You're validating the email as being present, which means that it would fail if the value is nil or an empty string.
In your rspec block that describes the case where the email should not be present, you're defining the email address as a string consisting of a single space. You actually want to make that a blank string, by removing the space:
before { #user.email = "" }
That will now fail the validation.
I have the following classes in Rails and am writing some rspec tests (any critiques are more than welcome as I'm a nOOb at rspec).
class User.rb
class User < ActiveRecord::Base
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true ,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => true },
:on => :create
end
and in factories.rb
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "my-name#{n}" }
sequence(:email) { |n| "blue#{n}#12blue.com" }
end
end
and in my rspec (users_spec.rb):
require 'spec_helper'
describe User do
let(:user) { FactoryGirl.build(:user) }
it { user.should be_valid }
it { user.should be_a(User) }
it { user.should respond_to(:email) }
it { user.email = " " }
it { user.should_not be_valid } # this is causing the error
end
and get
1) User
Failure/Error: it { user.should_not be_valid }
expected valid? to return false, got true
But based upon the validates, user should be not be valid. What is going on here? What am I not getting (and I know it's my fault)?
thx
I assume that the test failure surprises you because you think the user email should be " ".
In rspec every example is independent. This means that anything you did in a previous example is forgotten.
In your case your second to last example runs, builds a new, valid activerecord user whose email is "blue4#12blue.com", overwrites that email with " " and then passes since it makes no assertions.
Then your last example runs, builds a new, valid activerecord user who's email is "blue5#12blue.com" and fails because the user is valid, it's email has not been overwritten.
You probably want something like this:
it 'should validate the email' do
user.email = " "
user.should_not be_valid
end
What do I need to do to fix this? I am new to ruby on rails.
Error when rspec is ran
1) remember token should have a nonblank remember token
Failure/Error: before { #user.save }
NoMethodError:
undefined method `save' for nil:NilClass
# ./spec/models/user_spec.rb:125:in `block (2 levels) in <top (required)>'
user_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
.
.
.
it { should respond_to(:remember_token) }
.
.
.
describe "with a password that's too short" do
before { #user.password = #user.password_confirmation = "a" * 5 }
it { should be_invalid }
end
describe "return value of authenticate method" do
before { #user.save }
let(:found_user) { User.find_by_email(#user.email) }
describe "with valid password" do
it { should == found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
end
end
describe "remember token" do
before { #user.save }
it "should have a nonblank remember token" do
subject.remember_token.should_not be_blank
end
end
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
It looks like this block of code
describe "remember token" do
is outside the block
describe User do
...
end
If you move it inside the block, then it will have the before action fire that creates the #user object (which you then save in your own before block)
the #user variable will be not found, so
you should move describe "remember token" inside ->
describe User do
//current definitions
describe "remember token" do
before { #user.save }
it "should have a nonblank remember token" do
subject.remember_token.should_not be_blank
end
end
end