thoughtbot / shoulda-matchers

Simple one-liner tests for common Rails functionality
https://matchers.shoulda.io
MIT License
3.51k stars 912 forks source link

Rails 5 undefined method `validate_presence_of` #951

Closed rramsden closed 7 years ago

rramsden commented 8 years ago

I'm upgrading to from Rails 4.2.6 to 5.0.0 and receiving undefined method errors for all shoulda matchers. I'm using shoulda-matchers (3.1.1).

  2) Location
     Failure/Error: it { should validate_presence_of(:latitude) }

     NoMethodError:
       undefined method `validate_presence_of' for #<RSpec::ExampleGroups::Location:0x007ff81faa4de8>
     # ./spec/models/location_spec.rb:6:in `block (2 levels) in <top (required)>'
     # ./spec/rails_helper.rb:60:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:59:in `block (2 levels) in <top (required)>'

Edit

I managed to fix the error using the following. However, this should be handled automatically by the gem:

RSpec.configure do |config|
  config.include(Shoulda::Matchers::ActiveModel, type: :model)
  config.include(Shoulda::Matchers::ActiveRecord, type: :model)
end
sshaw commented 8 years ago

I'm having this problem with Rails 4.2.6. Explicitly including them solves it.

Could be due to the new way of configuring things in 3.X. I have a Rails 4.2.2 app using 2.8, no problem.

beslnet commented 8 years ago

look, you can fix it with this link https://www.sitepoint.com/learn-the-first-best-practices-for-rails-and-rspec/

Carlos-Montiel commented 7 years ago

Try this: Open your spec/rails_helper.rb file and configure shoulda-matchers to work with RSpec by pasting in the following:

require 'shoulda/matchers'

Shoulda::Matchers.configure do |config| config.integrate do |with| with.test_framework :rspec with.library :rails end end

source: https://www.sitepoint.com/learn-the-first-best-practices-for-rails-and-rspec/

monfresh commented 7 years ago

I'm seeing this as well after upgrading from 2.8.0 to 3.1.1, even when I configure RSpec with the aforementioned configs. The failing tests are all for Form Objects that include ActiveModel::Model. Any idea how to make those tests work?

monfresh commented 7 years ago

Never mind. Just found the documentation: https://github.com/thoughtbot/shoulda-matchers#availability-of-matchers-in-various-example-groups

mcmire commented 7 years ago

I'm closing this as I haven't been able to reproduce this in a Rails 5 app and everything works fine for me.

If anyone encounters this again, please see if you can reproduce this in a fresh Rails app and post a link to the repo so I can check it out. Thanks!

creatyvtype commented 7 years ago

I am encountering this problem without adding the above proposed solution, since you wanted to see it. https://github.com/UpStage-Community/upstage-api. Check out the devise-auth branch, run bundle, run rspec, and validate_uniqueness_of will fail.

creatyvtype commented 7 years ago

@rramsden's solution worked perfectly.

mcmire commented 7 years ago

@creatyvtype How by chance are you running your tests? Are you using Spring to do so?

creatyvtype commented 7 years ago

@mcmire just running "rspec". Nothin' fancy.

edsonlima commented 7 years ago

Although it is already closed, I came across this problem when I ran a spec for an ActiveModel::Model without define type: :model in Rspec.describe block.

The code bellow code fails with undefined_method error...

RSpec.describe NewInvestment do

  context 'validations' do
    it { is_expected.to validate_presence_of(:investment_product_id) }
   end
end

...but this one works perfectly

RSpec.describe NewInvestment, type: :model do

  context 'validations' do
    it { is_expected.to validate_presence_of(:investment_product_id) }
   end
end
margOnline commented 7 years ago

I had the same issue using rails 4.2.5 and fixed it using the suggestion by @edsonlima

PhilT commented 6 years ago

Just hit this issue on Ruby 2.4.2, Rails 5.1.4. gem 'shoulda-matchers', require: false and adding require 'shoulda/matchers' in spec/support/shoulda_matchers.rb solved it for me.

TorvaldsDB commented 6 years ago

Install the gem shoulda-matchers

group :test do
  gem 'shoulda-matchers'
end

Write following code in the rails_helper.rb

RSpec.configure do |config|
  config.include(Shoulda::Matchers::ActiveModel, type: :model)
  config.include(Shoulda::Matchers::ActiveRecord, type: :model)
end
mcmire commented 6 years ago

@TorvaldsDB Are you using the Shoulda::Matchers.configure block as listed in the README? That should take care of incorporating the matchers into the proper example groups for you without having to do it explicitly.

jmadkins commented 5 years ago

Can confirm I ran into this issue today and was able to fix it with @rramsden changes.

kaiomagalhaes commented 5 years ago

I'm using rails 6-beta and it is still happening

mcmire commented 5 years ago

@kaiomagalhaes What does your rails_helper.rb look like? Do you have Shoulda::Matchers.configure in this file or in a support file? Which version of shoulda-matchers are you using?

clubbavi commented 5 years ago

What about if I am using custom validator ?

 class AccountValidator
  include ActiveModel::Validations

  attr_reader :name, :badge
  validates :name, presence: true
  validates :badge, presence: true, type: { type: :string }

  def initialize(name, badge)
    @name = name
    @badge = badge
  end
end 

And the tests for it:

RSpec.describe AccountValidator do  
  subject { AccountValidator.new "Someone", "2324"}

  it { should validate_presence_of(:name) }
  it { should validate_presence_of(:badge) }
end

It always gives error:

undefined method `validate_presence_of' for #<RSpec::ExampleGroups::

Any suggestions ?

mcmire commented 5 years ago

@clubbavi The gem relies on your example groups to be tagged. RSpec will do this automatically for specs that you've placed in spec/models, spec/controllers or spec/routing. If your test for AccountValidator is not located here then you will need to tag it accordingly:

RSpec.describe AccountValidator, type: :model do
  # ...
end

This point is explained in the README but if there's something I need to make clearer, let me know. (EDIT: It occurs to me I don't have that exact point in the README so I'll add another section.)

clubbavi commented 5 years ago

@mcmire , yes, it's not located inside those folders. It has a folder structure of app/validators.

I have tried with the above solution, but error is it cannot find the model.

As well as:

require 'shoulda-matchers'

RSpec.configure do |config|
  config.include(Shoulda::Matchers::ActiveModel, type: :model)
  config.include(Shoulda::Matchers::ActiveRecord, type: :model)
end

Still I couldn't go through.

mcmire commented 5 years ago

Alright. What version of shoulda-matchers are you using? Do you have a Shoulda::Matchers.configure block in your rails_helper/spec_helper? What happens if you place the includes in your example group directly?

RSpec.describe AccountValidator do
  include Shoulda::Matchers::ActiveModel
  include Shoulda::Matchers::ActiveRecord
end
clubbavi commented 5 years ago

@mcmire ... we are using 4.0.0.rc1.

By adding the above code I am getting an error:

Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
       The matcher attempted to set :name on the AccountValidator to nil,
       but that attribute does not exist.
RSpec.describe AccountValidator do  
  include Shoulda::Matchers::ActiveModel
  include Shoulda::Matchers::ActiveRecord
  subject { AccountValidator.new "Someone", "2324"}

  it { should validate_presence_of(:name) }
  it { should validate_presence_of(:badge) }
end
mcmire commented 5 years ago

@clubbavi You can switch to 4.0.0 now, it's been released :) But okay. What does your validator class look like?

clubbavi commented 5 years ago

@mcmire ... I will try with the latest version.

My validator class:

 class AccountValidator
  include ActiveModel::Validations

  attr_reader :name, :badge
  validates :name, presence: true
  validates :badge, presence: true, type: { type: :string }

  def initialize(name, badge)
    @name = name
    @badge = badge
  end
end

I can write the rspec for initializers. But not sure of validates.

clubbavi commented 5 years ago

@mcmire , I just tested with the recent version. Still the same issue. For

it { should validate_presence_of(:name) }
Gives error:
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
       The matcher attempted to set :name on the AccountValidator to
       nil, but that attribute does not exist.

I have added in the configuration:

Shoulda::Matchers.configure do |config|
    config.integrate do |with|
      with.test_framework :rspec

      with.library :active_record
      with.library :active_model
    end
  end

& in the _sepc file:

RSpec.describe AccountValidator, type: :model do
...
end
mcmire commented 5 years ago

@clubbavi So it looks like your Validator class does not have attr_writers for the attributes. That is why it's telling you that the attribute doesn't exist. This is what I was saying before — this is not how the model matchers are designed to be used. If your class looked like this:

class AccountValidator
  include ActiveModel::Validations

  attr_accessor :name, :badge

  validates :name, presence: true
  validates :badge, presence: true, type: { type: :string }
end

then validate_presence_of and friends would work. (At that point though, I would argue you don't have a validator, but a model.)

sameera207 commented 5 years ago

Had the same issue, got it working by adding

RSpec.configure do |config|
  config.include(Shoulda::Matchers::ActiveModel, type: :model)
  config.include(Shoulda::Matchers::ActiveRecord, type: :model)
end

as mentioned by most of the people in this thread

shoulda-matchers-4.1.2
ruby 2.5.3
rails - 5.2.3
ravicious commented 4 years ago

In my case someone added rails_helper.rb to the project with Shoulda::Matchers configuration, but that helper wasn't required in any test file, so the method wasn't found.

I ended up just adding the Shoulda::Matchers config to spec_helper.rb, though in general it's recommended to have separate rails_helper and spec_helper. https://github.com/everydayrails/rails-4-1-rspec-3-0/issues/36#issuecomment-68499694