2
votes

Here is the rspec test to check the uniqueness of an email ( from http://ruby.railstutorial.org/chapters/modeling-users.html#code-validates_uniqueness_of_email_test )

require 'spec_helper'

describe User do

  before do
    @user = User.new(name: "Example User", email: "[email protected]")
  end
  .
  .
  .
  describe "when email address is already taken" do
    before do
      user_with_same_email = @user.dup
      user_with_same_email.save
    end

    it { should_not be_valid }
  end
end

As the author mentioned, I added

class User < ActiveRecord::Base
  .
  .
  .
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
                    uniqueness: true
end

to my user model and the tests pass.

But @user hasn't yet been saved to the database(I can't find @user.save statement anywhere is the code.) so, user_with_same_email is already unique since there is no other user with the same email in the database. Then how does it work?

I created something similar in the console. user_with_same_email.valid? returns false (with error "has already been taken"), but user_with_same_email.save still works. why?

2

2 Answers

4
votes

You can use the shoulda-matchers gem.

# spec/models/user_spec.rb
require 'spec_helper'

describe User, 'validations' do
  it { should validate_uniqueness_of(:email) }
  it { should validate_presence_of(:email) }
  it { should validate_format_of(:email).with_message(VALID_EMAIL_REGEX) }
end

Not positive about the last one, but it looks like it should work.

If you're using clearance, you can use the built-in email_validator functionality PR here

# app/models/user.rb
validates :email, presence: true, email: true
2
votes

Here's the source code for the be_valid matcher:

match do |actual|
  actual.valid?
end

As you can see, the matcher does not actually save the record, it just calls the method valid? on the instance. valid? checks whether validations will pass or not and if not, sets error messages on the instance.

In the case above, you are first (successfully) saving a user with the same email (user_with_same_email), which works since no user with that email has actually been saved yet. Then you're checking for validation errors on another user instance (@user) which has the same email, and that obviously fails even though you have not actually saved the duplicate record.

Regarding what you get in the console, the problem is likely that save does not return an error even if it fails. Try using save! instead.