thoughtbot / shoulda-matchers

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

"Undefined method" errors when running locally or in CI environment (Jenkins on Ubuntu) #174

Closed damonatbrightroll closed 11 years ago

damonatbrightroll commented 11 years ago

I also filed this at https://github.com/thoughtbot/shoulda/issues/224, but realized this may be a more appropriate location for it.

When running any of rspec, rake spec, bundle exec rspec, or bundle exec rake spec on my MacBook Pro, I see errors like this when running specs en masse:

$ rspec
....F.FFF.........F.FF..F.FFFFFF.....FF.FFF....F.FFF.....F.FFF......F.FF....F.F...........FFF.FFFF.......FFFFFF.........FF.FF.....F.FFFF.......F.FF.....FFFFF........F.FF..FF.FF.......FFF.FF.....F.FF.....F.FF......F....FF.FFFFF.......FFF....FFFFFF.....

Failures:

  1) Activation associations 
     Failure/Error: it { should belong_to(:line_item) }
     NoMethodError:
       undefined method `belong_to' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_1:0x007fcf9e4b94f0>
     # ./spec/models/activation_spec.rb:7:in `block (3 levels) in <top (required)>'

  2) Activation validations 
     Failure/Error: it { should validate_presence_of(:line_item_id) }
     NoMethodError:
       undefined method `validate_presence_of' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_2:0x007fcf9e5f0d00>
     # ./spec/models/activation_spec.rb:15:in `block (3 levels) in <top (required)>'

  3) Activation validations 
     Failure/Error: it { should validate_presence_of(:msg_id) }
     NoMethodError:
       undefined method `validate_presence_of' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_2:0x007fcf9e5ee438>
     # ./spec/models/activation_spec.rb:17:in `block (3 levels) in <top (required)>'

  4) Activation validations 
     Failure/Error: it { should ensure_length_of(:msg_id).is_at_most(Activation::MAXIMUM_LENGTH_OF_MSG_ID) }
     NoMethodError:
       undefined method `ensure_length_of' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_2:0x007fcf9e5f3b68>
     # ./spec/models/activation_spec.rb:18:in `block (3 levels) in <top (required)>'

etc. The errors seem only to occur against shoulda-style specs. However, I can run individual RSpec files without incident, e.g.,:

$ rspec spec/models/activation_spec.rb 
..............

Top 10 slowest examples (0.03596 seconds, 94.2% of total time):
  Activation associations 
    0.01183 seconds ./spec/models/activation_spec.rb:7
  Activation validations 
    0.00784 seconds ./spec/models/activation_spec.rb:15
  Activation validations should be valid
    0.00515 seconds ./spec/models/activation_spec.rb:11
  Activation validations 
    0.00318 seconds ./spec/models/activation_spec.rb:18
  Activation validations 
    0.00314 seconds ./spec/models/activation_spec.rb:17
  Activation attributes 
    0.00215 seconds ./spec/models/activation_spec.rb:30
  Activation defaults status 
    0.00084 seconds ./spec/models/activation_spec.rb:24
  Activation attributes 
    0.0007 seconds ./spec/models/activation_spec.rb:30
  Activation attributes 
    0.00056 seconds ./spec/models/activation_spec.rb:30
  Activation attributes 
    0.00056 seconds ./spec/models/activation_spec.rb:30

Finished in 0.03919 seconds
14 examples, 0 failures

Here is my Gemfile:

source 'http://rubygems.org'
source 'http://gems.btrll.com'

gem 'rails', '3.2.8'

# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'

gem 'brxutils'
gem 'mysql2'
gem 'state_machine'

# squeel grants additional ActiveRecord querying goodness without needing to embed SQL in code.
gem 'squeel'

# safe_attributes lets us overcome some legacy column names (e.g., 'errors') that collide with standard Rails-generated
# method names.
gem 'safe_attributes'

# strong_parameters gem should be used to prepare for Rails 4.0.
# See http://weblog.rubyonrails.org/2012/3/21/strong-parameters/
gem 'strong_parameters'

gem 'jquery-rails'
gem 'ar_query_builder', '1.0.1'

gem 'umsclient', '>= 0.4.1', :require => 'umsclient/frameworks/warden'

# used to integrate with other web services
gem 'rest_request', '2.10.1'

# used for role-based authorization
gem 'cancan'

# used for authentication
gem 'rails_warden'

# templating gem for json-based responses
gem 'rabl'

# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

# To use Jbuilder templates for JSON
# gem 'jbuilder'

# Use unicorn as the app server
# gem 'unicorn'

# Deploy with Capistrano
# gem 'capistrano'

# To use debugger
# gem 'ruby-debug19', :require => 'ruby-debug'

gem 'rq_request'
gem 'rq_client', '3.9.1'

gem 'redis'

# Used for stats collection with Graphite
gem 'statsd-ruby', '~> 0.4.0', github: 'jeremy/statsd-ruby', :require => 'statsd'

# needed for BrightRoll's build/deployment system
gem 'brpkg'

# solr support for full-text searching
gem 'sunspot_rails'

# used together with apache/nginx to serve rails applications
gem 'passenger', '3.0.13'

group :test do
  # factory_girl_rails makes it easy to create factories for unit tests.
  gem 'factory_girl_rails'

  # shoulda-matchers provides simpler syntax than vanilla RSpec in some cases.
  gem 'shoulda-matchers', '>= 1.4.0'

  # simplecov provides code coverage report in coverage/index.html.
  gem 'simplecov', :require => false

  # Allow to use/mock solr in tests
  gem 'sunspot-rails-tester'
end

group :development, :test do
  gem 'rspec-rails'

  # local solr server for development env
  gem 'sunspot_solr'

  # used by solr to show indexing progress
  gem 'progress_bar'

  # pretty-formats tables in console
  gem 'hirb'
end

More info:

$ ruby -v
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.1.0]
$ rails --version
Rails 3.2.8

What's particularly troublesome is that I've got another project with basically the exact same Gemfile (the latter was used as a template for the former) and I've never seen this problem until now.

More information, after running with rspec -b:

Failures:

  1) Activation associations 
     Failure/Error: it { should belong_to(:line_item) }
     NoMethodError:
       undefined method `belong_to' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_1:0x007fc284896928>
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-expectations-2.11.3/lib/rspec/matchers/method_missing.rb:9:in `method_missing'
     # ./spec/models/activation_spec.rb:7:in `block (3 levels) in <top (required)>'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example.rb:113:in `instance_eval'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example.rb:113:in `block in run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example.rb:253:in `with_around_each_hooks'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example.rb:110:in `run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:378:in `block in run_examples'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:374:in `map'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:374:in `run_examples'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:360:in `run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:361:in `block in run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:361:in `map'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/example_group.rb:361:in `run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/command_line.rb:28:in `block (2 levels) in run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/command_line.rb:28:in `map'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/command_line.rb:28:in `block in run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/reporter.rb:34:in `report'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/command_line.rb:25:in `run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/runner.rb:69:in `run'
     # /Users/damon/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/rspec-core-2.11.1/lib/rspec/core/runner.rb:8:in `block in autorun'

I am also seeing this in our (Jenkins) CI environment.

drapergeek commented 11 years ago

Can you post your spec_helper as well? I can't see why this issue would occur so I'm hoping that can provide some insight.

damonatbrightroll commented 11 years ago
require 'simplecov'
SimpleCov.start 'rails'

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'shoulda/matchers'
Dir[File.dirname(__FILE__) + "/spec_helpers/*.rb"].each {|file| require file }

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

DUMMY_ORG_ID = 12345
DUMMY_VALUE = 'some random string'
EMPTY_STRING = ''

RSpec.configure do |config|
  # ## Mock Framework
  #
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
  #
  # config.mock_with :mocha
  # config.mock_with :flexmock
  # config.mock_with :rr
  config.mock_with :rspec

  config.include(ControllerMacros, :type => :controller)

  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  # If true, the base class of anonymous controllers will be inferred
  # automatically. This will be the default behavior in future versions of
  # rspec-rails.
  config.infer_base_class_for_anonymous_controllers = false

  # FactoryGirl configuration:
  config.include FactoryGirl::Syntax::Methods

  # From https://makandracards.com/makandra/950-speed-up-rspec-by-deferring-garbage-collection
  config.before(:all) do
    DeferredGarbageCollection.start
  end
  config.after(:all) do
    DeferredGarbageCollection.reconsider
  end

  # Sunspot configuration (mock session)
  $original_sunspot_session = Sunspot.session
  config.before do
    Sunspot.session = Sunspot::Rails::StubSessionProxy.new($original_sunspot_session)
  end

  # Sunspot configuration: add ':solr => true' rspec metadata to allow example groups that use this metadata flag
  # to  use the original sunspot session
  config.before :each, :solr => true do
    Sunspot::Rails::Tester.start_original_sunspot_session
    Sunspot.session = $original_sunspot_session
    Sunspot.remove_all!
  end

  config.before(:suite) do
    # From http://robots.thoughtbot.com/post/21719164760/factorygirl-3-2-so-awesome-it-needs-to-be-released
    ActiveSupport::Notifications.subscribe('factory_girl.run_factory') do |name, start, finish, id, payload|
      execution_time_in_seconds = finish - start

      if execution_time_in_seconds >= 0.5
        $stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}"
      end
    end
  end
end
damonatbrightroll commented 11 years ago

Here is an entire spec file as well, if that helps. Line numbers of failing tests have changed somewhat from what I pasted above, but the checks are the same.

require 'spec_helper'

describe Activation do
  let(:activation) { Activation.new(:line_item_id => 1, :msg_id => DUMMY_VALUE, :status => Activation::Status::PENDING) }

  describe 'associations' do
    it { should belong_to(:line_item) }
  end

  describe 'validations' do
    it 'should be valid' do
      activation.should be_valid
    end

    it { should validate_presence_of(:line_item_id) }

    it { should validate_presence_of(:msg_id) }
    it { should ensure_length_of(:msg_id).is_at_most(Activation::MAXIMUM_LENGTH_OF_MSG_ID) }

    it { should ensure_inclusion_of(:status).in_array(Activation::Status.values) }
  end

  describe 'defaults' do
    subject { activation }

    its(:status) { should == Activation::Status::PENDING }
  end

  describe 'attributes' do
    [:id, :line_item_id, :msg_id, :status, :errors_text, :warnings_text, :created_at,
     :updated_at].each do |expected_attribute|
      it { should respond_to(expected_attribute) }
    end
  end
end
drapergeek commented 11 years ago

@damonatbrightroll - can you send me an email : draper-at-thoughtbot-dot-com

thibaudgg commented 11 years ago

We also get this issue on the Semaphore CI, have you been able to fix it? Thanks!

damonatbrightroll commented 11 years ago

No luck as yet. @drapergeek had two suggestions for us:

  1. Remove simplecov.
  2. Disable "require 'shoulda/matchers'" from our spec_helper.rb.

The former had no effect. The latter immediately broke the build in environments that had otherwise been working. Continuing to investigate, but for now we are disabling all shoulda-related specs until we can resolve this.

damonatbrightroll commented 11 years ago

@thibaudgg Can you please post your files as I did above so we can compare notes? If you can also paste the platform(s) on which you're building, that would be useful as well.

thibaudgg commented 11 years ago

Removing the branch and re-adding it seems to have done the trick on Semaphore CI.

@damonatbrightroll We doesn't do anything special in our config, we just have gem 'shoulda-watcher in your Gemfile in test group.

damonatbrightroll commented 11 years ago

@thibaudgg What do you mean by "removing the branch and re-adding it?" In git or something else?

thibaudgg commented 11 years ago

@damonatbrightroll it's a feature of Semaphore CI, not in GIt.

davidjoliver commented 11 years ago

I'm using Jenkins and I'm seeing this as well. @damonatbrightroll any word yet? @thibaudgg I wonder if there is an equivalent...measure one can take within Jenkins...(?)

damonatbrightroll commented 11 years ago

@davidjoliver: No progress yet, unfortunately. Various shoulda tests remain commented out or replaced with (more verbose) vanilla RSpec for now, particularly should have_many, should have_one, should belong_to, should ensure_inclusion_of, should ensure_length_of, should accept_nested_attributes_for, should validate_presence_of, should validate_uniqueness_of, and possibly one or two others I'm forgetting offhand.

markoa commented 11 years ago

Just to clarify what @thibaudgg mentioned - removing the branch and re-adding it on Semaphore technically means that a repo (together with the cached bundle) is deleted and cloned again. So I think there's some randomness going on.

drapergeek commented 11 years ago

Unfortunately I have not been able to replicate this. If possible I would like to see someone create a bare bones project to replicate this issue. If a failure could be seem without using CI that would also be helpful even if we have to be specific about the OS and setup. Without being able to duplicate the issue my hands are somewhat tied at the moment.

damonatbrightroll commented 11 years ago

I am on vacation this week but will take a stab at it next week.

damonatbrightroll commented 11 years ago

I've tried but haven't been able to create a barebones project that demonstrates the bad behavior on my MBP. Anyone else able to do so?

cade commented 11 years ago

I just ran into this issue yesterday, so I was determined to figure out what was going on. I'm not sure if the root cause of your problem is the same as mine, but I was able to create a bare bones project that replicates the issue: https://github.com/cade/shoulda-matchers-goes-boom

Instructions are in the README for how to reproduce and artificially 'fix' the problem. Hope this helps.

damonatbrightroll commented 11 years ago

Thanks, @cade!

damonatbrightroll commented 11 years ago

@drapergeek Any progress?

kevTheDev commented 11 years ago

+1 for any progress?

damonatbrightroll commented 11 years ago

Post-Thanksgiving bump.

plribeiro3000 commented 11 years ago

I had this same problem a while ago on a rack project. I solved it by stoping loading shoulda-matchers at Gemfile and loading it inside spec_helper.rb

Gemfile

  gem 'shoulda-matchers', :require => false

spec_helper.rb

  require 'shoulda-matchers'

Hope this helps.

damonatbrightroll commented 11 years ago

@plribeiro3000 Tried that to no avail.

damonatbrightroll commented 11 years ago

This seems to be fixed by the merge to master resulting from https://github.com/thoughtbot/shoulda/issues/221#issuecomment-9261177.

gabebw commented 11 years ago

Fantastic! I'll close this then.