why is rspec telling me that this user is valid - ruby-on-rails

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

Related

Rspec test help - email uniqueness test failing yet it works in my app

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.

Chapter 6 of RailsTutorial.org by Michael Hartl Rspec Blank Email check

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 just can't get my valid password test to pass

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)

Writing Tests for Model Name Uniqueness

I'm have a tutorial model which belongs to a user a model. I would like the tutorial titles to be unique on per user level. So two users can have tutorials with the same titles but a user can't have two tutorials with the same title. My test is failing but I know that I'm correcting filtering out titles that are repeated. What is wrong with my test?
# model - tutorial.rb
class Tutorial < ActiveRecord::Base
attr_accessible :title
belongs_to :user
validates :user_id, presence: true
validates :title, presence: true, length: { maximum: 140 }, uniqueness: { :scope => :user_id }
end
# spec for model
require 'spec_helper'
describe Tutorial do
let(:user) { FactoryGirl.create(:user) }
before do
#tutorial = FactoryGirl.create(:tutorial, user: user)
end
subject { #tutorial }
describe "when a title is repeated" do
before do
tutorial_with_same_title = #tutorial.dup
tutorial_with_same_title.save
end
it { should_not be_valid }
end
end
# rspec output
Failures:
1) Tutorial when a title is repeated
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/tutorial_spec.rb:50:in `block (3 levels) in <top (required)>'
The problem with the test is this line:
it { should_not be_valid }
This spec checks valid? on the subject of your test, which is #tutorial - which is valid.
Suggested Refactoring:
describe Tutorial do
let(:user) { FactoryGirl.create(:user) }
before do
#tutorial = FactoryGirl.create(:tutorial, user: user)
end
subject { #tutorial }
describe "when a title is repeated" do
subject { #tutorial.dup }
it { should_not be_valid }
end
end

Testing carrierwave keeps returning "Image can't be blank"-error

I've been implementing carrierwave, which works great in the browser. However, my tests keep returning this:
Error
1) Item
Failure/Error: it { should be_valid }
expected valid? to return true, got false
# ./spec/models/item_spec.rb:36:in `block (2 levels) in <top (required)>'
factories.rb
include ActionDispatch::TestProcess
FactoryGirl.define do
sequence(:email) { |n| "User#{n}#example.com"}
factory :user do
name "John doe"
email
password "foobar"
password_confirmation "foobar"
end
factory :list do
name "Lorem ipsum"
user
end
factory :item do
image { fixture_file_upload(Rails.root.join('spec', 'support', 'test_images', 'google.png'), 'image/png') }
title "Shirt"
link "www.example.com"
list
end
end
item_spec.rb
require 'spec_helper'
describe Item do
let(:user) { FactoryGirl.create(:user) }
let(:list) { FactoryGirl.create(:list) }
before do
#item = list.items.build(title: "Lorem ipsum")
#item.valid?
puts #item.errors.full_messages.join("\n")
end
subject { #item }
it { should respond_to(:title) }
it { should respond_to(:list_id) }
it { should respond_to(:list) }
it { should respond_to(:image) }
it { should respond_to(:remote_image_url) }
its(:list) { should == list }
it { should be_valid }
describe "when list_id not present" do
before { #item.list_id = nil }
it { should_not be_valid }
end
describe "when image not present" do
before { #item.image = "" }
it { should_not be_valid }
end
describe "with blank title" do
before { #item.title = " " }
it { should_not be_valid }
end
describe "with title that is too long" do
before { #item.title = "a" * 141 }
it { should_not be_valid }
end
end
item.rb
class Item < ActiveRecord::Base
attr_accessible :link, :list_id, :title, :image, :remote_image_url
belongs_to :list
mount_uploader :image, ImageUploader
validates :title, presence: true, length: { maximum: 140 }
validates :list_id, presence: true
validates_presence_of :image
end
I have a image in the spec/support/test_images folder called google.png.
I'm very new to rails, and any help is thus much appreciated!
it { should be_valid }
is failing because (as you might expect) the subject is not valid. You need to find out why it is invalid. Try something like this:
it "should be valid" do
subject.valid?
subject.errors.should be_empty
end
Now the example will fail, but the error message will be more descriptive.
Another way to approach this is to add pry to your project. Then add binding.pry where you'd like to open a console:
it "should be valid" do
subject.valid?
binding.pry
subject.errors.should be_empty
end
Now you can inspect your test objects to figure out how the validation failed.
I feel quite stupid. Forgot to attach an image, which obviously caused the validation to fail.
Had to change:
before do
#item = list.items.build(title: "Lorem ipsum")
#item.valid?
puts #item.errors.full_messages.join("\n")
end
To:
before do
#item = list.items.build(title: "Lorem ipsum")
#item.image = fixture_file_upload('/test_images/google.png', 'image/png')
end

Resources