Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoiding conflicts with shoulda-matchers #209

Open
zedtux opened this issue Feb 20, 2018 · 9 comments
Open

Avoiding conflicts with shoulda-matchers #209

zedtux opened this issue Feb 20, 2018 · 9 comments
Labels

Comments

@zedtux
Copy link

zedtux commented Feb 20, 2018

I have a Rails application composed of a mixture of ActiveRecord and Mongoid models.

I was able to write my unit tests until now the having configured my rails_helper.rb file has the following:

# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] = 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in #{Rails.env} mode!") unless Rails.env.test?
require 'rspec/rails'

# Add additional requires below this line. Rails is not loaded until this point!
require 'devise'

require_relative 'support/helpers'

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  config.include Mongoid::Matchers, type: :model
  config.include Devise::TestHelpers, type: :controller
  config.include Helpers

  # 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

  # RSpec Rails can automatically mix in different behaviours to your tests
  # based on their file location, for example enabling you to call `get` and
  # `post` in specs under `spec/controllers`.
  #
  # You can disable this behaviour by removing the line below, and instead
  # explicitly tag your specs with their type, e.g.:
  #
  #     RSpec.describe UsersController, :type => :controller do
  #       # ...
  #     end
  #
  # The different available types are documented in the features, such as in
  # https://relishapp.com/rspec/rspec-rails/docs
  config.infer_spec_type_from_file_location!

  # Filter lines from Rails gems in backtraces.
  config.filter_rails_from_backtrace!
  # arbitrary gems may also be filtered via:
  # config.filter_gems_from_backtrace("gem name")

  config.before(:suite) do
    DatabaseCleaner[:active_record].strategy = :transaction
    DatabaseCleaner[:mongoid].strategy = :truncation
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end

    # Reset the operators table sequence in order to ensure created operator ID
    # will be 1 when creating one.
    # This is needed as the tests assumes the operator with ID 1 is the one
    # which is logged in.
    ActiveRecord::Base.connection.execute('ALTER TABLE operators AUTO_INCREMENT = 1')
  end
end

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

    # Choose one or more libraries:
    with.library :active_record
    with.library :action_controller
  end
end

But I have a conflict now that I want to use the validate_length_of matcher.

I have a spec file of an ActiveRecord model like this:

require 'rails_helper'

RSpec.describe MyModel, type: :model do
  describe 'Validations' do
    it { should validate_length_of(:name).is_at_most(255) }
  end
end

But it is failing with the following error:

  1) MyModel Validations
     Failure/Error: it { should validate_length_of(:name).is_at_most(255) }

     NoMethodError:
       undefined method `is_at_most' for #<Mongoid::Matchers::Validations::ValidateLengthOfMatcher:0x0000000bad3058>
     ...

Is there a way I can tell to RSpec this is a Mongoid model (type: :mongoid_model ?) which would load only the mongoid-rspec matchers?

@zedtux
Copy link
Author

zedtux commented Feb 20, 2018

Now I have another conflict on something working usually with the following test:

it { should belong_to(:city).inverse_of(:city_names) }

which fails with:

  1) Address::CityName Associations
     Failure/Error: it { should belong_to(:city).inverse_of(:city_names) }

     NoMethodError:
       undefined method `inverse_of' for #<Mongoid::Matchers::Associations::HaveAssociationMatcher:0x00000007f728d8>
     ...

@dblock
Copy link
Collaborator

dblock commented Feb 21, 2018

Interesting, I am not sure what to do about this, but looks like a regular name collision to me. Does this mean we should rename some functions here?

@zedtux
Copy link
Author

zedtux commented Feb 22, 2018

Well, I forgot to update this issue, but I solved it by changing the way to load the mongoid-rspec gem.

I changed the type attribute from the following line from my rails_helper.rb file:

...

RSpec.configure do |config|
  config.include Mongoid::Matchers, type: :model
  ...
end

from :model to :mongoid_model :

...

RSpec.configure do |config|
  config.include Mongoid::Matchers, type: :mongoid_model
  ...
end

Then the ActiveRecord models are described as type: :model and the Mongoid models are described as type: :mongoid_model and all seem to work fine.

2 options in my eyes:

  • Fixing the issue
  • Updating the README.md file with this trick when having conflicts.

@dblock
Copy link
Collaborator

dblock commented Feb 22, 2018

I would prefer both? If you're doing the minimum maybe PRing (2)?

@zedtux
Copy link
Author

zedtux commented Feb 23, 2018

I can do the README.md file update, but fixing the conflicts looks quite long as I never worked in this gem (understanding the architecture, the behaviour and finally think of a solution would take a very long time while this should be quick for the gem's authors).

@dblock
Copy link
Collaborator

dblock commented Feb 23, 2018

Whatever you can do @zedtux we'll appreciate it.

@jonekdahl
Copy link

We ran into this issue after upgrading to Mongoid 7 (from 6.4.4 to 7.0.5). We were using validate_presence_of from shoulda-matchers but it seems that it is no longer compatible:

NoMethodError:
       undefined method `macro' for #<Mongoid::Association::Embedded::EmbedsMany:0x00005624e88a7a88>
     # /home/[redacted]/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/shoulda-matchers-4.1.2/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb:330:in `collection_association?'

Since we use matchers from both libraries in our specs, we managed to work around the issue by changing the order of the included modules in rails_helper.rb. This way, whenever there's a naming conflict, we will have the mongoid matcher:

RSpec.configure do |config|

  # Mongoid matchers must be included after shoulda's active model support,
  # since we want the mongoid version of validate_presence_of from mongoid-rspec.
  config.include Shoulda::Matchers::ActiveModel, type: :model
  config.include Mongoid::Matchers, type: :model
end

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :action_controller
    # with.library :active_model # inclusion of Shoulda::Matchers::ActiveModel is done manually, see above
  end
end

@jonekdahl
Copy link

In #161 (which also deals with shoulda-matchers compatibilty), it is suggested that conflicting matchers should have an alias. Perhaps that could be a solution here?

@valmirosjunior
Copy link

Well, I forgot to update this issue, but I solved it by changing the way to load the mongoid-rspec gem.

I changed the type attribute from the following line from my rails_helper.rb file:

...

RSpec.configure do |config|
  config.include Mongoid::Matchers, type: :model
  ...
end

from :model to :mongoid_model :

...

RSpec.configure do |config|
  config.include Mongoid::Matchers, type: :mongoid_model
  ...
end

Then the ActiveRecord models are described as type: :model and the Mongoid models are described as type: :mongoid_model and all seem to work fine.

2 options in my eyes:

  • Fixing the issue
  • Updating the README.md file with this trick when having conflicts.

It worked, perfectly for us, thank you a lot @zedtux

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants