RSpec uniqueness email test fails with FactoryGirl - ruby-on-rails-3

Edit
Using the answers to the question I changed the test to the following which tests correctly and passes..
describe "when email is already taken" do
let(:user_with_same_email) { #user.dup }
before do
user_with_same_email.email.upcase!
user_with_same_email.save
end
it { user_with_same_email.should_not be_valid }
end
Note: Not using let(:user_with_same_email) { #user.dup } makes the test fail as it cannot find the variable user_with_same_email if it's simply duplicated in the before block as in the chosen answer to this question.
I have a User model and a user_spec.rb test file which has various validations against the User models attributes.
Previously I was writing the following at the top of my user_spec.rb file to test the User model:
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar88", password_confirmation: "foobar88")
end
...
I wanted to move this model creation to FactoryGirl so I created a factories.rb file:
FactoryGirl.define do
factory :user do
name "foo"
email { "#{name}#example.com" }
password "foobar99"
password_confirmation "foobar99"
end
end
I then changed my user_spec.rb:
describe User do
before do
#user = FactoryGirl.create(:user)
end
...
Now every test passes as before except one:
describe "when email 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
Now unless `FactoryGirl is skipping my email uniqueness validation I can't figure out what is going wrong here.
My User model validation code:
class User < ActiveRecord::Base
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i unless const_defined?(:VALID_EMAIL_REGEX)
has_secure_password
attr_accessible :name, :email, :password, :password_confirmation
has_many :programs
before_save { self.email.downcase! }
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }

The problem is that when you say it { should_not be_valid }, RSpec checks the subject. in this case the subject is User.new (you have "describe User" at the top so unless you specified something else this is the default).
You want to check the user_with_same_email for validity instead.
edit:
Try this, I think it might work:
describe "when email 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 { #user_with_same_email.should_not be_valid }
end

Looks like perhaps you're doing (or referencing) Michael Hartl's Rails Tutorial. Here's what my code looks like for what you're doing, so I hope it can be of use:
spec/models/user_spec.rb
describe User do
let(:user) { valid_user }
subject { user }
# ...
context "when email address is already taken" do
before { save_user(user) }
it { should_not be_valid }
end
# ...
end
spec/support/utilities.rb (to create a specific user)
def valid_user
User.new(name: "Example User",
email: "user#example.com",
password: "foobar",
password_confirmation: "foobar")
end
# ...
def save_user(user)
user_with_same_email = user.dup
user_with_same_email.email.upcase!
user_with_same_email.save
end
For reference: spec/factories.rb (to just create any old random user)
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:email) { |n| "person_#{n}#example.com" }
password "foobar"
password_confirmation "foobar"
# ...
end
# ...
end
Update: Found the answer you were looking for at this StackOverflow answer outlining the same problem. I tested it with my code as well and it worked for me.
Update 2: Changed my code around as well, using FactoryGirl.build for times when I want a user but don't want it saved to the database. This StackOverflow answer helped me understand.
spec/models/user_spec.rb
describe User do
let(:user) { FactoryGirl.create(:user) }
subject { user }
# ...
context "when email address is already taken" do
let(:user_with_same_email) do
FactoryGirl.build(:user, email: user.email)
end
subject { user_with_same_email }
before do
user_with_same_email.email.upcase!
user_with_same_email.save
end
it { should_not be_valid }
end
# ...
end
Thanks for asking this question. Gave me some food for thought and some refactoring to do in my own code.

Related

Rails MailForm Gem captcha validation not passing test

I am using the
MailForm Gem
to create a contact form for my app and everything seems to be working just fine so I decided to write some tests to make sure it stays that way.
class ContactsControllerTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "should send contact email" do
get contact_path
post contacts_path, params: { contact: {
name: "interested customer",
email: "interested#customer.com",
subject: "we are interested!",
message: "we are so interested!!!"
}}
assert_equal 1, ActionMailer::Base.deliveries.size
assert_redirected_to root_path
end
test "should not send invalid contact email" do
get contact_path
post contacts_path, params: { contact: {
name: "",
email: "",
subject: "",
message: ""
}}
assert_equal 0, ActionMailer::Base.deliveries.size
assert_template 'contacts/new'
end
test "should not send contact email with captcha filled" do
get contact_path
post contacts_path, params: { contact: {
name: "interested customer",
email: "interested#customer.com",
subject: "we are interested!",
message: "we are so interested!!!",
nickname: "not_blank"
}}
assert_equal 0, ActionMailer::Base.deliveries.size
assert_template 'contacts/new'
end
the first two tests pass while the third fails with the message
FAIL["test_should_not_send_contact_email_with_captcha_filled", ContactsControllerTest, 5.2574800989968935]
test_should_not_send_contact_email_with_captcha_filled#ContactsControllerTest (5.26s)
Expected: 0
Actual: 1
test/controllers/contacts_controller_test.rb:35:in `block in <class:ContactsControllerTest>'
My model looks like this.
class Contact < MailForm::Base
attribute :name, :validate => true
attribute :email, :validate => /\A([\w\.%\+\-]+)#([\w\-]+\.)+ ([\w]{2,})\z/i
attribute :subject
attribute :message
attribute :nickname, :captcha => true
def headers
{
:subject => "Contact: #{subject}" ,
:to => "myemail#mail.com",
:from => %("#{name}" <#{email}>)
}
end
end
My first thought was that the captcha validation is not stopping the mail from being sent. If someone could point out what I am missing I would appreciate it.
The mailform model returns valid, even if the nickname is given. But you can check if it is spam, which prohibits the mail from being sent.
I use this to check if the spam detection works(with rspec):
it 'should be marked spam if it contains :nickname' do
expect(FactoryGirl.build(:contact_with_nickname)).to be_spam
end

Using flash to notify user of previous registration

I'm trying to use my Users controller to notify the user when their email has already been used in a registration, but even when the email already exists, I still get the error "Plase validate your input and try again," rather than "You've already registered! Thanks for being enthusiastic!" Is using the controller not the create way of achieving this behavior?
In the rails console (assuming "foo#bar.com" is in the database"), when I use user = User.new(name:"Example", email:"foo#bar.com") then User.find_by_email(user.email) it does return the proper User entry, so I'm not sure if I'm on the right track and just executing it incorrectly or what. Any ideas?
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new(params[:user])
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = "Thanks for supporting cofind! We'll be in touch!"
redirect_to root_path
UserMailer.welcome_email(#user).deliver
else
if #user.email == User.find_by_email(#user.email)
flash[:error] = "You've already registered! Thanks for being enthusiastic!"
redirect_to root_path
else
flash[:error] = "Plase validate your input and try again."
redirect_to signup_path
end
end
end
end
user.rb:
class User < ActiveRecord::Base
attr_accessible :email, :name
before_save { |user| user.email = email.downcase }
validates :name, presence: true
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 }
end
this line
if #user.email == User.find_by_email(#user.email)
checks the user's email (a string) against a user record (an ActiveRecord object) which will always be false. You should change that to
if User.where(email: #user.email).exists?

error: The following untracked working tree files would be overwritten by merge:

Thanks in advance for the help!
I was just finishing up chapter 6 of Rails Tutorial:
http://ruby.railstutorial.org/chapters/modeling-users#sec-6_4
I ran:
❤ git checkout master
Which went fine and then I ran:
❤ git merge modeling-users
And got the following error:
Updating fc9f72a..90d1ba6
error: The following untracked working tree files would be overwritten by merge:
app/models/user.rb
Please move or remove them before you can merge.
Aborting
This is my app/models/user.rb file:
# == 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
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
before_save { |user| user.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 }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
end
And now my Sublime Text 2 user_spec.rb and application_helper_spec.rb files wont save and get the following errors:
Unable to save ~/code/rails_projects/sample_app/spec/models/user_spec.rb
user_spec.rb:
# == 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 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 }
describe "when name is not present" do
before { #user.name = " " * 51 }
it { should_not be_valid }
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#bax.cn]
addresses.each do |valid_address|
#user.email = valid_address
#user.should 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
#user.should 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 }
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 "when password confirmation is nil" do
before { #user.password_confirmation = nil }
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
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
and my application_helper_spec.rb file:
require 'spec_helper'
describe ApplicationHelper do
describe "full_title" do
it "should include the page title" do
full_title("foo").should =~ /foo/
end
it "should include the base title" do
full_title("foo").should =~ /^Ruby on Rails Tutorial Sample App/
end
it "should not include a bar for the home page" do
full_title("").should_not =~ /\|/
end
end
end
The error The following untracked working tree files would be overwritten by merge is happening because there is a app/models/user.rb file in the folder which is currently not added to any branch.
You can verify this by running the git status command; it should show an output similar to the following:
$ git status
# On branch master
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# app/models/user.rb
no changes added to commit (use "git add" and/or "git commit -a")
When you run git merge modeling-users, it is trying to merge the modeling-users branch to master branch. The modeling-users branch already has a copy of app/models/user.rb included in it. The merge command is making it overwrite the untracked version of the file with the one already within the modeling-users branch. Hence the error message to warn you about possibly losing something you want to keep.
As the git status message indicates, add the file to the master branch by running git add app/models/user.rb command.
The Unable to save ~/code/rails_projects/sample_app/spec/models/user_spec.rb message seems to be unrelated to this. Not much can be made out of the error message; perhaps there is more info somewhere else.
It might help to close the project and re-open it again. You may also want to try saving the files in a different editor (vim, perhaps?)

Rails Rspec & FactoryGirl testing Association

I have to model's where I accept Nested Attributes. I would like to build a test to make sure the nested attribute cant be blank etc. I really don't understand how I can make the test.
My two simple models:
# SeoMapping Model
class SeoMapping < ActiveRecord::Base
belongs_to :mappingtable, :polymorphic => true
attr_accessible :seo_url
validates :seo_url, :presence => true, :uniqueness => true
end
# Page Model
class Page < ActiveRecord::Base
has_one :seo_mappings, :as => :mappingtable, :dependent => :destroy
accepts_nested_attributes_for :seo_mappings
attr_accessible :content, :h1, :meta_description, :title, :seo_mappings_attributes
.........
end
Here are my factories for Page and Seo:
FactoryGirl.define do
factory :page do |f|
seo_mapping
f.title { Faker::Name.name }
f.h1 { Faker::Lorem.words(5) }
f.meta_description { Faker::Lorem.words(10) }
f.content { Faker::Lorem.words(30) }
end
end
FactoryGirl.define do
factory :seo_mapping do |f|
f.seo_url { Faker::Internet.domain_word }
end
end
And my tests:
require 'spec_helper'
describe Page do
it "has a valid factory" do
expect(create(:page)).to be_valid
end
# Cant get this spec to work?
it "it is invalid without a seo_url" do
page = build(:page)
seo_mapping = build(:seo_mapping, seo_url: nil)
page.seo_mapping.should_not be_valid
# expect(build(:page, :seo_mapping_attributes[:seo_url] => nil)).to_not be_valid
end
it "is invalid without a title" do
expect(build(:page, title: nil)).to_not be_valid
end
...............
end
Usually for this sort of thing I use a gem called shoulda_matchers. It lets you simply assert that your model validates presence of specific attributes.
it { should validate_presence_of(:seo_url) }
it { should validate_uniqueness_of(:seo_url) }
If you don't want to use the gem, try something like this:
seo_mapping = build(:seo_mapping, seo_url: nil)
page = build(:page, seo_mapping: seo_mapping)
page.should_not be_valid

Failing test in ruby tutorial (Michael Hartl)

I'm studying Michael Hartl's tutorial. I'm using RSPEC to run the test.
So far so good but it seems that I've hit the wall with the following example.
Here is the test that fails (it should pass):
describe "authenticate method" do
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
Just in case.
#user defined as:
before(:each) do
#user = User.create!(#attr)
end
#attr defined as:
before(:each) do
#attr = {
:name => "Example user",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
Entries in user.rb
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt unless has_password?(password)
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
Error message displayed when the test is failing
c:\RailsInstaller\work\apptwit>rspec spec/models/user_spec.rb
.................F
Failures:
1) User password validations password encryption authenticate method should return the user on email/password
Failure/Error: matching_user.should == #user
expected: #<User id: 1, name: "Example user", email: "user#example.com", created_at: "2011-12-07 19:08:23
ed_at: "2011-12-07 19:08:23", encrypted_password: "fbdbaf712fa1b6c925c4ab2192e73ac9f9d1bedf67630610d68...">
got: nil (using ==)
# ./spec/models/user_spec.rb:204:in `block (5 levels) in <top (required)>'
Finished in 221.37 seconds
18 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:202 # User password validations password encryption authenticate method should
he user on email/password match
I would appreciate any pointers,
Thanks a lot.
If matching_user is nil, then you might want to put some puts email and puts user.inspect statements in self.authenticate to debug it.
It seems as though it's either not able to find the user by email or your password is incorrect for some reason in the authenticate method.