Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby User Authentication with Rails Password Hashing and Sign In Using has_secure_password

'has_secure_password' causing user.save test to fail

I have just added the has_secure_password to my user model and run the rspec tests. Only one failed!? Odd, but not to worry. Adding the password and password_confirmation properties didn't change this.

The one that is failing is "requires a unique email (case insensitive)". The reason it is failing is down to the line expect(user.save).to be_true. This is also throwing deprecation warnings asking to be updated to be_truthy.

Using be_true or be_truthy gives the same error. Whether the tests fails because it expected: true, got: false or expected: truthy value, got: false is semantics. Indeed the deprecation was done because be_true was acting like be_truthy already.

With either bit of code, the expect(user.save).to be_true fails on the addition of has_secure_password makes this fail - presumably, there's some step to take before a user can be saved when using BCrypt, I'm not sure!

I can easily remove/amend the test if we can't get to the bottom of it, but I thought I'd flag this. Does anyone have any thoughts on how to fix it or what I've done wrong?

Thanks in advance,

Steve.

Seth Reece
Seth Reece
32,867 Points

I've been having this same problem for a few hours in my own application. It appears that everything works fine in development. The user is saved in the database and the email is lowercase. I'm using rspec 3.2.1, and Odot uses 2.0. I'm not sure if that's my issue, but it seems to be an issue with my test.

I've not got anywhere near adding to the database so I've got no idea if that works or not! My rspec is v2.99.2.

I shall plough on later this week to see if I can figure it all out. Let me know if you make progress, eh! ;-)

Seth Reece
Seth Reece
32,867 Points

It appear my failure is being cause by having two unique validations.

validates :email, presence: true,
                    uniqueness: true,
                    format: {
                      with: /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9\.-]+\.[A-Za-z]+\Z/
                    }

  validates :user_name, presence: true,
                        uniqueness: true

If I remove my uniqueness: true from :user_name my test passes. Not sure what's going on there.

Weird - surely the problem lies elsewhere?

1 Answer

Brandon Barrette
Brandon Barrette
20,485 Points

To answer your question about the uniqueness on the username, what could be happening is that since you are declaring a unique username, whenever you are testing with a user, rspec is going to create a brand new user each time. However, if you don't provide different unique usernames each time, that validation will fail and the user will not save.

Looking in my Odot files, this is my users.rb factory which generates the new users:

FactoryGirl.define do
  factory :user do
    first_name "First"
    last_name "Last"
    sequence :email do |n| 
      "user#{n}@example.com"
    end
    password "asdf1234"
    password_confirmation "asdf1234"
  end
end

Notice we have a sequence on the e-mail which will generate unique e-mails each time. So if you just add username like this:

FactoryGirl.define do
  factory :user do
    first_name "First"
    last_name "Last"
    username: "MyUsername"
    sequence :email do |n| 
      "user#{n}@example.com"
    end
    password "asdf1234"
    password_confirmation "asdf1234"
  end
end

This will fail, because each user will have the same username, causing the uniqueness to fail on the second user. The trick is to add a sequence just like the email:

FactoryGirl.define do
  factory :user do
    first_name "First"
    last_name "Last"
    sequence :username do |n|
      "MyUsername#{n}"
    end
    sequence :email do |n| 
      "user#{n}@example.com"
    end
    password "asdf1234"
    password_confirmation "asdf1234"
  end
end
Seth Reece
Seth Reece
32,867 Points

Brandon, that did work. Had to rewrite my hole models test though. Wasn't using factory girl. Had a felling that might be it, but seemed odd that a unique email passed just fine.