Rails code validation working but Rspec tests failing - ruby-on-rails

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

Related

rspec email_spec and jobs.. how does it work? I want to check whether the letter was sent.. and maybe email body

I have 'controllers/users_controller.rb'
def create
user = User.new(person_params)
if user.save
EmailSendJob.perform_later(user.email, 'random_password')
end
end
I have 'mailers/user_mailer.rb'
def user_new(user_to, password)
#password = password
mail to: user_to, subject: "Password for you"
end
I have 'jobs/email_send_job.rb'
def perform(email, password)
UserMailer.user_new(email, password).deliver_now
end
How do I do testing? Thank you
I saw 'email-spec', but can't understand it for my code
I have 'spec/controllers/users_controller_spec.rb'
require "spec_helper"
require "rails_helper"
require "email_spec"
describe UsersController do
User.delete_all
before(:all) do
#email_for_test = "222#222#mail.ru"
#user = User.new
#user.email = "2#2.ru"
#user.name = "test"
#user.save
end
it "create" do
post :create, params: {user: {email: #email_for_test,
name:"test",
description: "test"}}
user = User.last
expect(user.email).to eq(#email_for_test)
last_delivery = ActionMailer::Base.deliveries.last
last_delivery.body.raw_source.should include "This is the text of the email"
end
end
ActionMailer::Base.deliveries.last don't work
Maybe you have example. I would be very grateful
Try using ActionMailer::Base.deliveries.count
So for example:
it "sends an email" do
expect { UserMailer.user_new(email, password).deliver }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
I tend to use email-spec in feature specs. A test will look something like this:
require 'rails_helper'
feature 'Admin creates purchase' do
include ActionView::Helpers::NumberHelper
given(:purchase) { build(:purchase) }
given(:new_purchase) { Purchase.last }
scenario 'with valid input' do
click_link 'Purchases'
click_link 'New Purchase'
fill_in 'Name', with: purchase.name
fill_in 'Email', with: purchase.email
fill_in 'Quantity', with: purchase.quantity
perform_enqueued_jobs do
click_button 'Create Purchase'
expect(page).to have_content 'The purchase was completed successfully'
open_email(purchase.email)
expect(current_email).to have_subject 'Receipt from My Super Awesome Company'
expect(current_email).to have_content "Name #{purchase.name}"
expect(current_email).to have_content "Total Charge #{number_to_currency(new_purchase.total)}"
end
end
end
You could probably do similar sorts of content checking in a controller spec.
ok, I think it is answer good.. I found also something -
it "sends an email" do
email = UserMailer.password_new(#email_for_test, "password_test").deliver
m = ActionMailer::Base.deliveries
expect(m.count).to eq(1)
expect(email.html_part.body).to include("Hello, your new password")
end
email = ActionMailer::Base.deliveries.last
(email.text_part || email.html_part || email).body

How do I write this spec in MiniTest/shoulda syntax?

I have this spec that I want to translate to MiniTest.
describe User do
subject { build(:user, provider: 'foo') }
# don't validate presence of password when provider is present
it do
should_not validate_presence_of(:password)
end
end
I tried this. I am getting an error of undefined method 'should_not' for UserTest
class UserTest < ActiveSupport::TestCase
def setup
#user = build_stubbed(:user)
end
test "responds to name" do
assert_respond_to #user, :name
end
should validate_presence_of(:password)
test "do not validate presence of password when provider is present" do
build_stubbed(:user, provider: 'foo')
should_not validate_presence_of(:password)
end
end
I want to change the context for one test, where the subject gets a provider attribute, which should disable the presence validator on the password field.
Here's the full error:
UserTest#test_presence_of_password:
NoMethodError: undefined method `should_not' for #<UserTest:0x007feaa82c1c68>
test/models/user_test.rb:45:in `block in <class:UserTest>'
I found that the better way to do this is to revert to good old MiniTest:
test "uniqueness of email with a different provider" do
email_user = create(:user, email: "foo#bar.com")
facebook_user = build_stubbed(:facebook_user, email: "foo#bar.com")
assert facebook_user.valid?, "should be valid with same email if provider is different"
end
Take a look at the minitest-rails-shoulda gem. If you use it I assume the test would look like this:
describe User do
subject { build_stubbed(:user) }
it { must validate_presence_of(:password) }
describe "when a provider is present" do
subject { build_stubbed(:user, provider: 'foo') }
it { wont validate_presence_of(:password) }
end
end

Why does this specification not work as expected?

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.

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.

Resources