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

rspec-rails 3.5.1 + rails 5, running whole spec suite results in (seemingly) random LoadErrors #1677

Closed
grandconjuration opened this issue Jul 22, 2016 · 2 comments

Comments

@grandconjuration
Copy link

grandconjuration commented Jul 22, 2016

$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
$ rails -v
Rails 5.0.0
$ rspec -v
3.5.1

We're migrating from Rails 4 to Rails 5 and we're having a problem when running the whole spec suite.
When I run rspec using the rspec (without arguments) command I randomly get
LoadError: Unable to autoload constant Importer::Transformer, expected [rails_dir]/lib/importer/transformer.rb to define it
Of course, /lib/importer/transformer.rb does define it

For example, I get this error (sometimes) when running the whole suite
LoadError: Unable to autoload constant Importer::Exceptions, expected [rails_dir]/lib/importer/exceptions.rb to define it # ./spec/transforms/any_to_ton_transform_spec.rb:76:inblock (2 levels) in <top (required)>'`

But if I then run rspec spec/transforms/any_to_ton_transform_spec.rb specifically all tests pass and there is no LoadError, so it seems something is up with the autoloader if I run the whole suite but not a single spec file. Am I doing something wrong in my spec_helper or rails_helper or is something else going on?

I have tried to add any relevant information in this post and I'm curious if anyone else has this problem or whether I did something to cause this? Any help would be appreciated, thanks :)

spec_helper.rb source

require 'simplecov'
SimpleCov.start
RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups

  config.example_status_persistence_file_path = "spec/examples.txt"
  config.disable_monkey_patching!
  config.order = :random
  Kernel.srand config.seed

  config.filter_run_including focus: true
  config.run_all_when_everything_filtered = true
end

rails_helper.rb source

ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'

require 'support/controllers_helper'
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

ActiveRecord::Migration.maintain_test_schema!

RSpec.configure do |config|
  config.use_transactional_fixtures = true
  config.infer_spec_type_from_file_location!
  config.filter_rails_from_backtrace!
  config.include ControllersHelper, type: :controller

  config.include FactoryGirl::Syntax::Methods

  config.include JsonSpec::Helpers
  config.include FileHelpers
  config.include ParserHelper, type: :parser
  config.include ActionHelper, type: :action

  config.after { Temping.teardown }
end

config/application.rb source

require File.expand_path('../boot', __FILE__)

require "rails"
require "active_model/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"

Bundler.require(*Rails.groups)

module MyApplication
  class Application < Rails::Application
    ActiveSupport.parse_json_times
    config.autoload_paths += %W[
      #{config.root}/app/serializers
      #{config.root}/app/transforms
      #{config.root}/app/transforms/concerns
      #{config.root}/app/transforms/sub_transforms
      #{config.root}/app/parsers
      #{config.root}/app/parsers/wagons
      #{config.root}/app/actions
      #{config.root}/lib
    ]

    config.action_dispatch.perform_deep_munge = false

    config.active_job.queue_adapter = :delayed_job

    config.middleware.insert_after Rails::Rack::Logger, Rack::Cors do
      allow do
        origins '*.myapplicat.io', 'localhost:*', '*'
        resource '*',
          headers: :any,
          methods: %i(get post put patch delete head options)
      end
    end
  end
end

any_to_ton_transform.rb source

class AnyToTonTransform
  def self.execute(value)
    return value if value.is_a?(Numeric) || value.nil?

    value = value.gsub('.', '') if %w(, .).all? { |c| value.include? c }

    BigDecimal.new(value.gsub(',', '.'))
  rescue
    raise Importer::Exceptions::TransformError,
      'value could not be converted into number'
  end
end

any_to_ton_transform_spec.rb source

require 'rails_helper'

RSpec.describe AnyToTonTransform do

  def execute(value)
    described_class.execute(value)
  end

  example 'ignores numbers' do
    expect(execute 12).to be 12
  end

  example 'ignores nil' do
    expect(execute nil).to eq nil
  end

  example '"0" -> 0' do
    expect(execute '0').to eq 0
  end

  example '"1" -> 1' do
    expect(execute '1').to eq 1
  end

  example '"22" -> 22' do
    expect(execute '22').to eq 22
  end

  example '"123.456" -> 123.456' do
    expect(execute '123.456').to eq 123.456
  end

  example '"456,7" -> 456.7' do
    expect(execute '456,7').to eq 456.7
  end

  example '"2.346,1" -> 2346.1' do
    expect(execute '2.346,1').to eq 2346.1
  end

  example '"2.346,8" -> 2346.8' do
    expect(execute '2.346,8').to eq 2346.8
  end

  example '"7,7" -> 7.7' do
    expect(execute '7,7').to eq(7.7)
  end

  example '"7,1" -> 7.1' do
    expect(execute '7,1').to eq(7.1)
  end

  example '"7.7" -> 7.7' do
    expect(execute '7.7').to eq(7.7)
  end

  example '"7.0" -> 7' do
    expect(execute '7.0').to eq(7)
  end

  example '"7.1" -> 7.1' do
    expect(execute '7.1').to eq(7.1)
  end

  example '"7.22" -> 7.22' do
    expect(execute '7,22').to eq(7.22)
  end

  example '"7.231" -> 7.231' do
    expect(execute '7.231').to eq(7.231)
  end

  it "throws an exception for invalid input" do
    expect{
      execute({})
    }.to raise_error(Importer::Exceptions::TransformError)
  end
end

/lib/importer/exceptions.rb source

module Importer
  module Exceptions
    class CustomError < StandardError
      def severity
        self.is_a?(FatalError) ? :fatal : :warning
      end

      def as_json(_ = nil)
        { message: message, severity: severity }
      end

      def to_json
        JSON.dump(as_json)
      end
    end

    FatalError       = Class.new(CustomError)
    InvalidCellError = Class.new(FatalError)
    InvalidRowError  = Class.new(FatalError)
    InvalidFileError = Class.new(FatalError)

    WarningError   = Class.new(CustomError)

    class TransformError < WarningError
      attr_accessor :value,
                    :transform

      def as_json(_ = nil)
        {
          message:   message,
          severity:  severity,
          value:     value,
          transform: transform
        }
      end
    end
  end
end
@JonRowe
Copy link
Member

JonRowe commented Jul 22, 2016

Honestly I'm not sure theres much we can do to help with this, the Rails autoloader is notorious for producing odd issues like this, but I can suggest a few things that might work as a work around:

  • try eager loading in the test environment (e.g. like production) so it loads things earlier on (or hopefully at least consistently?)
  • explicitly requiring files in your specs

@grandconjuration
Copy link
Author

Thanks for the suggestions, I'll give them a try! 👍

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

No branches or pull requests

2 participants