Why does this specification not work as expected? - ruby-on-rails

I looked on stack overflow for an answer and found a few people who posted on similar problems but the solutions didn't help. From what I can tell my code is exactly right.
I am following the tutorial on:
http://ruby.railstutorial.org/chapters/modeling-users#code-validates_uniqueness_of_email_case_insensitive_test
require 'spec_helper'
describe User do
before { #user = User.new(name: "Example User", email: "user#example.com") }
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should be_valid }
describe "when name is not present" do
before { #user.name = " " }
it { should_not be_valid }
end
describe "when email is not present" do
before { #user.email = " " }
it { should_not be_valid }
end
describe "when name is too long" do
before { #user.name = "a" * 51 }
it { should_not be_valid }
end
describe "when email format is invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo. foo#bar_baz.com foo#bar+baz.com]
addresses.each do | invalid_address |
#user.email = invalid_address
expect(#user).not_to be_valid
end
end
end
describe "when email format is valid" do
it "should be valid" do
addresses = %w[user#foo.COM A_US-ER#f.b.org frst.lst#foo.jp a+b#baz.cn]
addresses.each do | valid_address |
#user.email = valid_address
expect(#user).to be_valid
end
end
end
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" do
# expect(#user).not_to be_valid
#end
it { should_not be_valid }
end
end
The last specification is failing.
My User validation is as such:
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]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensative: false }
end
I followed the tutorial perfectly and yet I can't seem to get this validation to work properly. I know this tutorial is a bit old and I may be using a few newer gems. I also know this isn't the best way to do this type of validation but the tutorial is just making it really simple at first. What is going on here?
Thank you!
EDIT:
There is no error. The validation is giving a failure when it should be success.
Failures:
1) User when email address is already taken should not be valid
Failure/Error: it { should_not be_valid }
expected #<User id: nil, name: "Example User", email: "user#example.com", created_at: nil, updated_at: nil> not to be valid
# ./spec/models/user_spec.rb:58:in `block (3 levels in <top (required)>'
Finished in 0.09887 seconds
1 example, 1 failure
Edit again. user_spec.rb has been completely pasted in quest. Sorry I am really new at ruby and didn't realize what might be needed to help troubleshoot.

If you continue reading the tutorial you realise that the author says that the test is still not completed to be passed. That's because the current validation is still case sensitive.
Adding uniqueness: { case_sensitive: false } makes the test passed as expected
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }

I think it just solved itself. Maybe someone can explain what just happened. I decided to just continue with the tutorial and added gem 'bcrypt-ruby', '3.1.2' to the gemfile. Then I ran bundle install.
Then I tried to run rspec spec/ and it returned an error:
Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=development' to resolve this issue.
But when I ran rake db:migrate nothing happened. SO I googled and found this SO post which had a solution.
I ran:
rake test:prepare
rake db:migrate
and then all tests passed.
Perhaps my test environment went crazy on me? I still don't know what just happened.

You can add user_with_same_email.valid? before .save and provide its output, but I believe it will be true, because:
you haven't changed the id of #user
you just change the case of email, but it is as also valid, so "A#A.COM", and "a#a.com" are the same, and valid.
#valid returns true since :email is present, have good Regexp, and still unique.

Related

Ruby on Rails User Test Fails

Doing the Ruby on Rails Tutorial and I'm getting strange error message when running the test.
My user_test.rb:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User", email: "user#example.com", password: "wordpass", password_confirmation: "wordpass")
end
test "should be valid" do
assert #user.valid?
end
test "name should be present" do
#user.name=" "
assert_not #user.valid?
end
test "email should be present" do
#user.email= " "
assert_not #user.valid?
end
test "name should not be too long" do
#user.name= "a" * 51
assert_not #user.valid?
end
test "email should not be too long" do
#user.email= "a" * 256
assert_not #user.valid?
end
test "email validation should accept valid addresses" do
valid_addresses = %w[user#example.com USER#foo.COM A_US-ER#foo.bar.org
first.last#foo.jp alice+bob#baz.cn]
valid_addresses.each do |valid_address|
#user.email = valid_address
assert #user.valid? , "#{valid_address.inspect} should be valid"
end
end
test "email validation should reject invalid addresses" do
invalid_addresses = %w[user#example,com user_at_foo.org user.name#example.
foo#bar_baz.com foo#bar+baz.com]
invalid_addresses.each do |invalid_address|
#user.email = invalid_address
assert_not #user.valid?, "#{invalid_address.inspect} should be invalid"
end
end
test "email addresses should be unique" do
duplicate_user = #user.dup
duplicate_user.email = #user.email.upcase
#user.save
assert_not duplicate_user.valid?
end
test "password should be a minimum length" do
#user.password = #user.password_confirmation = "a" * 5
assert_not #user.valid?
end
end
The user.rb :
class User < ActiveRecord::Base
attr_accessor :name, :email , :password, :password_confirmation
before_save { self.email = email.downcase }
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 }
has_secure_password
validates :password, length: { minimum: 6 }
end
One of the errors seems to be from the user definition, but I cannot find the source of error. Is it something due related to "validates" in user.rb file ? Any help will be much appreciated !!
FAIL["test_email_validation_should_accept_valid_addresses", UserTest, 0.061796445]
test_email_validation_should_accept_valid_addresses#UserTest (0.06s)
"user#example.com" should be valid
test/models/user_test.rb:34:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:32:in `each'
test/models/user_test.rb:32:in `block in <class:UserTest>'
FAIL["test_should_be_valid", UserTest, 0.080466737]
test_should_be_valid#UserTest (0.08s)
Failed assertion, no message given.
test/models/user_test.rb:8:in `block in <class:UserTest>'
Thanks in advance for your help !
You are right, one of the errors is due to #user being invalid in the first test. Do the following in rails console, this should print out errors on user model:
#user = User.new(name: "Example User", email: "user#example.com", password: "wordpass", password_confirmation: "wordpass")
#user.valid?
pp #user.errors
Try different regex in user.rb
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
well everything looks fine. just run the command
rake db:test:prepare
and if this doesnt work either then
$rake db:delete
rake db:create
rake db:migrate
first one worked for me

Rails code validation working but Rspec tests failing

Can anyone see what's wrong about the Rspec? All of my tests for invalid emails are failing (i.e., they are supposed to not be valid but somehow are in Rspec), but in the actual code, it works fine, no emails that are bad are allowed. The validation even works in my console....
Two notes:
I realize the formatting isn't ideal... will work on that later. This was just me writing things individually without going through and DRYing it up yet.
I also realize there exists an easier way to validate these things individually in the model, but I wanted very custom messages without any reference to the attribute, and I disliked the roundabout way that I've read that needs to be done.
Model code
validate :create_validation, on: :create
def create_validation
errors[:base] << "Please enter a valid email address to continue" if self.email.blank? || (( /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i =~ self.email) == nil)
end
#The RegEx is from Michael Hartl's tutorial
Spec code
describe Signup do
before do
#signup = FactoryGirl.create(:signup)
end
subject { #signup }
describe "email tests" do
describe "invalid tests" do
# blank email
before { #signup.email = " " }
it { should_not be_valid }
# invalid format email
addresses = %w[user#foo..com, user#foo,com user_at_foo.org example.user#foo. foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
before { #signup.email = invalid_address }
it { should_not be_valid }
end
end
end
end
Factory girl code in spec helper
FactoryGirl.define do
factory :signup do |f|
f.email { Faker::Internet.email }
#... other attributes
end
end
As I understood code you validate only on create! But in tests you created object in before block and it is persisted in your tests - so validation is skipped.
Try this variant
describe Signup do
before do
#signup = FactoryGirl.build(:signup)
end
subject { #signup }
describe "email tests" do
describe "invalid tests" do
# blank email
before { #signup.email = " " }
it { should_not be_valid }
# invalid format email
addresses = %w[user#foo..com, user#foo,com user_at_foo.org example.user#foo. foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
before { #signup.email = invalid_address }
it { should_not be_valid }
end
end
end
end
I hope validation will be triggered for built but not saved record.
PS Or you can remove option on: :create

RSpec failling in email should not be valid

I'm new to all this rails/rspec and so on, so im trying to follow some tests that I had found in the project that i'm working and make sense, they were generated by scaffold but the app is very different and no tests were updated so im re-doing every test.
The problem is when I try to validate that invalid emails should make the user invalid it fails.
I know that it's ok because I already done tests and they pass without no problem.
It is possible that I'm looking at this the wrong way but ...
User_spec
describe "when email format is invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.
foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
#user.email = invalid_address
#user.should_not be_valid
end
end
end
User_test
test "fail save new user with an corrupt email" do
user = User.new
user.name = 'Someone Name'
user.password = '12345678'
user.email = 'Someone Name#gmail..com'
assert !user.save , "User was saved with an corrupt email"
end
The validation is done by devise and the failure message is "expected valid? to return false, got true"
---- edit ---
My user setup
...
describe User do
before :each do
#user = User.new(name: "Name of Names", email:"someone1#somewhere.com",password: "foobarfoobar", password_confirmation: "foobarfoobar" )
end
...
You could do something along the lines of this:
context "validations" do
describe "email" do
subject { FactoryGirl.create(:user, email: email) }
context "with an valid address" do
let(:email) { "valid#email.com" }
it { should be_valid }
end
context "with an invalid email address" do
let(:email) { "invalid.email.com" }
it { should_not be_valid }
end
end
end
So I'm assuming you are using FactoryGirl to create an valid user object. Just create new tests per invalid email, this way you can see which tests are passing and which aren't. Then you could TDD your way to green.

Understand Test-Driven Development with Rspec and FactoryGirl

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.

Rails, undefined method error. But I can use this method in rails console

In my rspec testing. I got NoMethodError:
undefined method `password_digest='.
But in rails console. I do can use .password_digest method.
I am so confused. I searched google. Got sever threads about this. even in stackoverflow. But those don't help. I do have password_digest as a field in my database.
I can even set my password_digest
see:
1.9.2-p290 :005 > user.password_digest
=> "$2a$10$X8CSsstOqZKKA6qVHpW9.uH5Lzd7dxfGNCAxvIbePpcfBg8KFbD4y"
1.9.2-p290 :006 > user.password_digest = 1
=> 1
1.9.2-p290 :007 > user.password_digest
=> 1
And also, in my app. everything seems fine. That's weird.
Please help...
1.9.2-p290 :002 > User.first.password_digest
User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
=> "$2a$10$NCfX2SgKeqGARJ68StxRJuCbbbK7g18n5FPxbHY5THwg4pAdHUvui"
1.9.2-p290 :003 >
[5]+ Stopped rails console
luke#Macbook-Pro~/Documents/workspace/RoR/rails_projects/sample_app2012$ bundle exec rspec spec/models/user_spec.rb
DEPRECATION WARNING: The InstanceMethods module inside ActiveSupport::Concern will be no longer included automatically. Please define instance methods directly in #<Class:0x000001029f6688> instead. (called from <top (required)> at /Users/luke/Documents/workspace/RoR/rails_projects/sample_app2012/spec/models/user_spec.rb:14)
DEPRECATION WARNING: The InstanceMethods module inside ActiveSupport::Concern will be no longer included automatically. Please define instance methods directly in #<Class:0x000001029f6688> instead. (called from <top (required)> at /Users/luke/Documents/workspace/RoR/rails_projects/sample_app2012/spec/models/user_spec.rb:14)
FFFFFFFFFFFFFFFFFFFFF
Failures:
1) User
Failure/Error: #user = User.new(name: "Example User", email: "user#example.com",
NoMethodError:
undefined method `password_digest=' for #<User:0x00000100beee60>
# ./spec/models/user_spec.rb:17:in `new'
# ./spec/models/user_spec.rb:17:in `block (2 levels) in <top (required)>'
..... There are 19 more failures, to save space, i didn't paste them here.
And this is my user_spec.rb code:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
require 'spec_helper'
describe User do
before(:each) 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(:remember_token) }
it { should respond_to(:authenticate) }
it { should be_valid }
describe "when name is not present" do
before { #user.name = " " }
it { should_not be_valid }
end
describe "when email is not present" do
before { #user.email = " " }
it { should_not be_valid }
end
describe "when name is too long" do
before { #user.name = "a" * 51 }
it { should_not be_valid }
end
describe "when email format is valid" do
valid_addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
valid_addresses.each do |valid_address|
before { #user.email = valid_address}
it {should be_valid }
end
end
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 password is not present" do
before { #user.password = #user.password_confirmation = " " }
it {should_not be_valid}
end
describe "when password doesn't match confirmation" do
before { #user.password_confirmation = "mismatch" }
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 == 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
describe "remember token" do
before { #user.save }
its(:remember_token) { should_not be_blank }
end
end
end
And this is my user.rb under modles:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :email, :password, :password_confirmation
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}
end
Was just stuck on this for a while as well.
Don't forget
rake db:test:prepare after new migrations!
class User has method password_diges but has no method password_digest=
this is not the same methods, so instance of User shall not be responsible on password_digest
it shoud return password_digest but should_not respond_to password_digest i think
You can use User.first.password_digest in console because password_digest and password_digest= isn't the same methods.
password_digest= same as:
def password_digest=(digest)
#password_digest = digest
end
otherwise password_digest:
def password_digest
#password_digest
end
In your console, you're trying the password_digest method, not the password_digest= method. Are you able to execute the following in the console? User.first.password_digest = "$2a$10$NCfX2SgKeqGARJ68StxRJuCbbbK7g18n5FPxbHY5THwg4pAdHUvui"
I think your issue may be rooted in the fact that you're specifying only certain attributes in your User model as being attr_accessible. Try adding :password_digest to that list and see if your test passes.
Hi I am new to RoR and I learned RailsTutorial and encountered this issue too. After a long search in Google I found my db/test.sqlite3 hasn't been updated. which means there was 'password_digest' in my db/development.sqlite3 but there wasn't that column in my db/test.sqlite3.
And after I run the command 'rake db:reset' and 'rake db:test:prepare', the issue was gone.
I had the exactly same issue yesterday. The console in development and in test environment work well. And the web app itself works great as well. But the test (almost the same test as the question described) fail for no method error.
I've tried to stop spring, recreate test database, kill memcached. But it doesn't work.
Finally, I added some data into the fixture file test/fixtures/users.yml.
harry:
name: Harry
email: harry#example.com
password_digest: <%= User.digest('password') %>
It was empty before. Then the tests passed. I made the user.yml empty afterwards.The tests still passed.
I don't know why yet. But hope it helps.

Resources