shroy
shroy

Reputation: 918

Rspec 3: Format regex breaks validation test

I am testing to build user authentication from scratch.

I started by testing and setting up validations and they all passed. Once I added the format: { with: ... } method, my test suite broke.

Here's the failure along with the code:

Failures:

  1) User validations requires an email
     Failure/Error: expect(user).to validate_presence_of(:email)
     NoMethodError:
       undefined method `validate_presence_of' for #<RSpec::ExampleGroups::User::Validations:0x007fae7f5551b0>
     # ./spec/models/user_spec.rb:14:in `block (3 levels) in <top (required)>'

  2) User validations requires a unique email
     Failure/Error: expect(user).to validate_uniqueness_of(:email)
     NoMethodError:
       undefined method `validate_uniqueness_of' for #<RSpec::ExampleGroups::User::Validations:0x007fae83193b90>
     # ./spec/models/user_spec.rb:18:in `block (3 levels) in <top (required)>'

  3) User validations requires a unique email (case insensitive)
     Failure/Error: expect(user).to validate_uniqueness_of(:email)
     NoMethodError:
       undefined method `validate_uniqueness_of' for #<RSpec::ExampleGroups::User::Validations:0x007fae83168670>
     # ./spec/models/user_spec.rb:23:in `block (3 levels) in <top (required)>'

Finished in 0.04888 seconds (files took 1.2 seconds to load)
6 examples, 3 failures

spec/models/user_spec.rb

require 'rails_helper'

RSpec.describe User, :type => :model do
    let(:valid_attr) { attributes_for(:user) }

    context "validations" do
        let(:user) { User.new(valid_attr) }

        before do
            User.create(valid_attr)
        end

        it "requires an email" do
            expect(user).to validate_presence_of(:email)
        end

        it "requires a unique email" do
            expect(user).to validate_uniqueness_of(:email)
        end

        it "requires a unique email (case insensitive)" do
            user.email = "[email protected]"
            expect(user).to validate_uniqueness_of(:email)
        end

        it "requires the email to look like an email" do
            user.email = "brown"
            expect(user).to_not be_valid
        end
    end

    describe "#downcase_email" do
        it "makes the email attribute lowercase" do
            user = User.new(valid_attr.merge(email: "[email protected]"))
            expect{ user.downcase_email }.to change{ user.email }.
                from("[email protected]").
                to("[email protected]")
        end

        it "downcases an email before saving" do
            user = build(:user)
            user.email = "[email protected]"
            expect(user.save).to be true
            expect(user.email).to eq("[email protected]")
        end
    end
end

Factory Girl users.rb

FactoryGirl.define do
  factory :user do
    first_name "Charlie"
    last_name "Brown"
    email "[email protected]"
    password "charbar1234"
    password_confirmation "charbar1234"
  end

end

models/user.rb

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

    before_save :downcase_email

    def downcase_email
        self.email = email.downcase
    end
end

Upvotes: 1

Views: 1337

Answers (2)

Becky
Becky

Reputation: 207

A better way is to use the URI object:

validates_format_of :website_url, with: URI.regexp

Upvotes: 1

froderik
froderik

Reputation: 4808

Rspec is probably confused by this extra format parameter so it cannot dynamically create the validates_*_of methods properly. You could either rewrite your test to be more direct:

it "requires an email" do
  expect(user).to be_valid
  # other test on error message here perhaps?
end

or put the format parts on its own validates line:

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

Upvotes: 1

Related Questions