0

I've been struggling with this for a few hours now and i can't find an answer anywhere.

Basically i'm writing specs for my models using shoulda and for some reason even though my let works just fine and the actual application works fine(confirmed manually and with request specs), if i include:

it { expect(user).to validate_uniqueness_of :email }

in my describe block, i will get the following error:

1) User attributes should require case sensitive unique value for email
 Failure/Error: it { expect(user).to validate_uniqueness_of :email }
 RuntimeError:
   Password digest missing on new record
 # ./spec/models/user_spec.rb:14:in `block (3 levels) in <top (required)>'

I've gotten to a point where solving this spec is holding me back, which is never a good thing, since the implementation already works.

It would be great if someone could help me out here because i REALLY don't want to start skipping tests that are failing for some obscure reason just to get things moving.

I feel as if rspec was 'ignoring' the before method that is called for has_secure_password to save the digest but i'm not sure. I'm just assuming because if the request spec worked fine, this is doing something i'm not aware of.

I'm using rails 4.0.0

Below is the relevant code to this. I appreciate any help. Thanks

user_spec.rb

require  'spec_helper'

describe User do

  let(:user) { 
    user = User.new(:email => 'example@example.com', 
                    :username => 'theo', 
                    :password => 'secretpass',
                    :password_confirmation => 'secretpass')}

  describe "attributes" do

    it { expect(user).to validate_presence_of :email } 
    it { expect(user).to validate_uniqueness_of :email }

    it { expect(user).to validate_presence_of :username }
    it { expect(user).to validate_uniqueness_of :username }

    it "saves are saved" do
      user.save!
      expect(user).to be_valid
    end

  end  


end

user.rb

class User < ActiveRecord::Base
  has_secure_password


  validates :email, :username, presence: true, uniqueness: true
  validates :password, presence: true, :on => :create


end

users_controller.rb

class UsersController < ApplicationController
  def index
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)

    if @user.save
      redirect_to root_path, :notice => 'You have successfully signed up.'
    else
      render 'new'
    end
  end

  private

  def user_params
    params.require(:user).permit(:email, :username, :password, :password_confirmation)
  end
end
Theo Felippe
  • 279
  • 1
  • 2
  • 17

3 Answers3

5

Please make sure you have password_digest exist in your User schema, details please see here

create_table “users”, force: true do |t|
    ……
    t.text     “password_digest”
    …..
end

If it works in your development but not test, please check if you do the rake db:migration in test env, I mean RAILS_ENV=test rake db:migrate.


!!!Updated -- related to shouda-matchers gocha

It seems a bug for shoulda-matchers, cause when you do this the test will pass:

it "should be validate uniq of email " do
    user.save
    expect(user).to validate_uniqueness_of :email
end

the reason why I am doing the user.save is because of this , otherwise it will create a record for you, that will cause the error you got:

# https://github.com/thoughtbot/shoulda-matchers/blob/master/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb#L126
 def create_record_in_database(options = {})
      if options[:nil_value]
        value = nil
      else
        value = "arbitrary_string"
      end

      @subject.class.new.tap do |instance|
        instance.send("#{@attribute}=", value)
        instance.save(:validate => false) # the error happens here, not in your code
      end
    end

So above all, this is probably a bug of shouda-matchers, though I didn't have time to figure out how to fix it, for now you can use this method as a work around, or use some other method to test this.

Hope it helps :-)

Mike Li
  • 3,336
  • 2
  • 22
  • 27
  • 29decibel, that didn't fix either. I tried adding a new record on the console in the test env and it works fine. For some reason it only fails in the tests. :/ – Theo Felippe Jul 14 '13 at 01:17
  • @TheoFelippe Can you comment out `it { expect(user).to validate_presence_of :email }` to see if only this test fails or all of them will fail? – Mike Li Jul 14 '13 at 01:48
  • Mike Li, i have done that already. presence_of is the only ones that fail. currently i have them commented out and all pass as they should. – Theo Felippe Jul 14 '13 at 01:51
  • What the failing error you got? the same ? `Password digest missing on new record ` – Mike Li Jul 14 '13 at 02:03
  • Yes, exactly what i posted above. :/ – Theo Felippe Jul 14 '13 at 02:08
  • Can you tell me which rspec gem you use to make `expect(user).to validate_presence_of :email` working ? cause the user should not contains `validate_presence_of` right? – Mike Li Jul 14 '13 at 02:09
  • This looks like a bug to me. I just wrote another spec that instantiates an user, just like i did before and it creates the user just fine. The problem is on validations. maybe shoulda? :/ – Theo Felippe Jul 14 '13 at 02:12
  • That's 'shoulda-matchers'. basically it lets you write validations in your model spec the same way you would do in older versions of rails. It reads well for documentation. What i'm wondering right now is whether their latest version support rails 4.. because that, i did not check... – Theo Felippe Jul 14 '13 at 02:14
  • @TheoFelippe I checked the source code of `shoulda-matchers`, please see the updated answer, thanks! – Mike Li Jul 14 '13 at 03:03
  • yeah, i suspected it would be something like that. thanks for taking the time dude. – Theo Felippe Jul 15 '13 at 15:05
0

Probably a bug in shoulda-matchers, maybe related to this one https://github.com/thoughtbot/shoulda-matchers/issues/290

Cheers

phron
  • 1,795
  • 17
  • 23
0

Answer to your question is here: https://github.com/thoughtbot/shoulda-matchers/issues/371

In short, here is the underlying issue:

It appears that the Rails 4 version of has_secure_password adds a before_create to ensure that password_digest is filled in; however, the Rails 3 version does not have this check.

Yaron
  • 610
  • 6
  • 13