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

FrozenError when adding a factory called :product #303

Closed
ksnyder opened this issue Oct 14, 2018 · 6 comments
Closed

FrozenError when adding a factory called :product #303

ksnyder opened this issue Oct 14, 2018 · 6 comments

Comments

@ksnyder
Copy link

ksnyder commented Oct 14, 2018

On Rails 5.2.1, and FactoryGirl 4.8.0 (factory_girl_rails same version), I copied an existing factory called :product_spec and renamed it :product.

After doing that, the specs won't run. I get this error on every spec file:

An error occurred while loading ./spec/models/user_spec.rb.
Failure/Error: require File.expand_path('../../config/environment', __FILE__)

FrozenError:
  can't modify frozen Array

The contents of the factory don't matter -- if I completely empty the factory, I still get the error. If I rename the factory to :product1, the error goes away. Whether or not the class name is passed doesn't make a difference. It appears :product is some kind of reserved word in factory_girl?

Code that fails:

factory :product, class: Product do
#
end

@composerinteralia
Copy link
Collaborator

No, product isn't a reserved word. I think you may be running into #247. Can you provide a backtrace, or any additional information? I haven't been able to reproduce this myself, which makes it difficult to debug.

@ksnyder
Copy link
Author

ksnyder commented Oct 15, 2018

Here's the backtrace:

Sorry for the font, but the included # and ` characters in the stacktrace seem to trigger Markdown...

An error occurred while loading ./spec/models/user_spec.rb.
Failure/Error: require File.expand_path('../../config/environment', __FILE__)

FrozenError:
  can't modify frozen Array
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/actionpack-5.2.1/lib/action_dispatch/middleware/stack.rb:76:in `insert'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/actionpack-5.2.1/lib/action_dispatch/middleware/stack.rb:76:in `insert'
# ./config/initializers/cors.rb:8:in `<top (required)>'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `load'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `block in load'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `load'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/engine.rb:657:in `block in load_config_initializer'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/notifications.rb:170:in `instrument'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/engine.rb:656:in `load_config_initializer'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/engine.rb:614:in `block (2 levels) in <class:Engine>'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/engine.rb:613:in `each'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/engine.rb:613:in `block in <class:Engine>'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/initializable.rb:32:in `instance_exec'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/initializable.rb:32:in `run'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/initializable.rb:61:in `block in run_initializers'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/initializable.rb:50:in `each'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/initializable.rb:50:in `tsort_each_child'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/initializable.rb:60:in `run_initializers'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/railties-5.2.1/lib/rails/application.rb:361:in `initialize!'
# ./config/environment.rb:5:in `<top (required)>'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `require'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `block in require'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `require'
# ./spec/rails_helper.rb:3:in `<top (required)>'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `require'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `block in require'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:287:in `require'
# ./spec/models/user_spec.rb:1:in `<top (required)>'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `load'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `block in load'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:253:in `load_dependency'
# /Users/byofuel/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.1/lib/active_support/dependencies.rb:281:in `load'
No examples found.

@composerinteralia
Copy link
Collaborator

Yeah, this seems to be similar to #247. The error comes from calling insert on a frozen array here: https://github.com/rails/rails/blob/5-2-1/actionpack/lib/action_dispatch/middleware/stack.rb#L76. This error isn't pointing to anything obvious in factory_bot or factory_bot_rails. This issue on Rails also seems related: rails/rails#33745

@composerinteralia
Copy link
Collaborator

Since there isn't anything obviously pointing to factory_bot, and there is some indication that it might have been fixed (see #247 (comment)) I'm going to close this issue for now. If you are able to share a sample app that reproduces the error, I would be happy to help debug. Thanks!

@composerinteralia
Copy link
Collaborator

composerinteralia commented Oct 31, 2018

Actually I think I was able to reproduce this after all!

I wrote this invalid factory:

Factory.define do
  factory :user 
  factory :user
end

When we run the test suite RSpec starts by loading every spec file. It goes something like this:

  • RSpec loads first_spec.rb
  • first_spec.rb requires rails_helper.rb
  • rails_helper.rb requires config/environment.rb
  • config/environment.rb initializes the Rails application, which includes inserting all the middlewares, then freezing the middleware Stack
  • After the app initializes, factory_bot_rails loads the factory definitions
    config.after_initialize do
    FactoryBot.find_definitions
    end
  • Loading definitions fails with a FactoryBot::DuplicateDefinitionError
  • As a result of this failure config/environment.rb and rails_helper.rb don't ever fully load (i.e. they don't appear in $LOADED_FEATURES)
  • RSpec rescues the error and reports it
    https://github.com/rspec/rspec-core/blob/9d1d0d6aeb8dc87ff4e584cdcf9e8b1a234da2ae/lib/rspec/core/configuration.rb#L2035-L2037
  • But RSpec doesn't give up, it continues trying to load the next spec -> second_spec.rb
  • second_spec.rb requires rails_helper.rb
  • rails_helper.rb requires config/environment.rb
  • 'config/environment.rb' tries to initializes the Rails application again, but it can't insert middleware because the stack is already frozen
  • We get a FrozenError, which RSpec rescues and reports
  • RSpec continues on, and we likely see a whole bunch of FrozenErrors

The FactoryBot::DuplicateDefinitionError does get reported correctly, but I can see how this would be confusing, and how one might miss the FactoryBot::DuplicateDefinitionError because it gets pushed out of sight by a bunch of FrozenErrors.

@composerinteralia
Copy link
Collaborator

composerinteralia commented Oct 31, 2018

Actually, it seems like rspec/rspec-core#2568 addressed exactly this

jamesjefferies added a commit to Energy-Sparks/energy-sparks that referenced this issue Mar 3, 2019
imjoehaines added a commit to bugsnag/bugsnag-ruby that referenced this issue Jul 23, 2020
This should never happen in normal operation, but is possible when
running RSpec tests

See thoughtbot/factory_bot_rails#303 (comment)
and #534

Essentially this happens:
1. RSpec boots Rails
2. Rails boot process errors
3. Rails freezes middleware
4. RSpec moves on to the next test file
5. Bugsnag tries to add middleware but fails because it's frozen
6. The stacktrace blames Bugsnag for this test failure
7. goto 4

Supressing this error doesn't solve the problem as it's likely another
frozen error will happen in some other library/component, but we want
to avoid people thinking this is caused by Bugsnag. The stacktrace
does point out the actual problem, but only in the very first error,
which can get buried under the frozen errors that happen for each
test file
imjoehaines added a commit to bugsnag/bugsnag-ruby that referenced this issue Jul 23, 2020
This should never happen in normal operation, but is possible when
running RSpec tests

See thoughtbot/factory_bot_rails#303 (comment)
and #534

Essentially this happens:
1. RSpec boots Rails
2. Rails boot process errors
3. Rails freezes middleware
4. RSpec moves on to the next test file
5. Bugsnag tries to add middleware but fails because it's frozen
6. The stacktrace blames Bugsnag for this test failure
7. goto 4

Supressing this error doesn't solve the problem as it's likely another
frozen error will happen in some other library/component, but we want
to avoid people thinking this is caused by Bugsnag. The stacktrace
does point out the actual problem, but only in the very first error,
which can get buried under the frozen errors that happen for each
test file
imjoehaines added a commit to bugsnag/bugsnag-ruby that referenced this issue Jul 23, 2020
This should never happen in normal operation, but is possible when
running RSpec tests

See thoughtbot/factory_bot_rails#303 (comment)
and #534

Essentially this happens:
1. RSpec boots Rails
2. Rails boot process errors
3. Rails freezes middleware
4. RSpec moves on to the next test file
5. Bugsnag tries to add middleware but fails because it's frozen
6. The stacktrace blames Bugsnag for this test failure
7. goto 4

Supressing this error doesn't solve the problem as it's likely another
frozen error will happen in some other library/component, but we want
to avoid people thinking this is caused by Bugsnag. The stacktrace
does point out the actual problem, but only in the very first error,
which can get buried under the frozen errors that happen for each
test file
imjoehaines added a commit to bugsnag/bugsnag-ruby that referenced this issue Jul 27, 2020
This should never happen in normal operation, but is possible when
running RSpec tests

See thoughtbot/factory_bot_rails#303 (comment)
and #534

Essentially this happens:
1. RSpec boots Rails
2. Rails boot process errors
3. Rails freezes middleware
4. RSpec moves on to the next test file
5. Bugsnag tries to add middleware but fails because it's frozen
6. The stacktrace blames Bugsnag for this test failure
7. goto 4

Supressing this error doesn't solve the problem as it's likely another
frozen error will happen in some other library/component, but we want
to avoid people thinking this is caused by Bugsnag. The stacktrace
does point out the actual problem, but only in the very first error,
which can get buried under the frozen errors that happen for each
test file
aeroastro added a commit to aeroastro/rspec-rails that referenced this issue Mar 9, 2022
When there is a bug in the code on the load,
RSpec catches the exception, aborts the spec file,
proceeds to the following spec file, and retries `require`
with partially loaded Rails application.

In some cases, we see many identical errors,
such as `NameError`, which are noisy but help developers
find the bug. In other cases, due to retrying `require`,
we see many unrelated errors like `FrozenError`,
which flood the result and flush out the original cause at the top.

The following comment describes this issue in detail.
thoughtbot/factory_bot_rails#303 (comment)

This patch prevents unrelated exceptions by the fail-fast feature of rspec/core.
rspec/rspec-core#2568
aeroastro added a commit to aeroastro/rspec-rails that referenced this issue Mar 9, 2022
When there is a bug in the code on the load,
RSpec catches the exception, aborts the spec file,
proceeds to the following spec file, and retries `require`
with partially loaded Rails application.

In some cases, we see many identical errors,
such as `NameError`, which are noisy but help developers
find the bug. In other cases, we see many unrelated errors,
typically `FrozenError` on finalized objects,
which push out the original exception raised only from the first spec.

The following comment describes this issue in detail.
thoughtbot/factory_bot_rails#303 (comment)

This patch prevents unrelated exceptions by the fail-fast feature.
rspec/rspec-core#2568
aeroastro added a commit to aeroastro/rspec-rails that referenced this issue Mar 9, 2022
When there is a bug in the code on the load,
RSpec catches the exception, aborts the spec file,
proceeds to the following spec file, and retries `require`
with partially loaded Rails application.

In some cases, we see many identical errors,
such as `NameError`, which are noisy but help developers
find the bug. In other cases, we see many unrelated errors,
typically `FrozenError` on finalized objects,
which push out the original exception raised only from the first spec.

The following comment describes this issue in detail.
thoughtbot/factory_bot_rails#303 (comment)

This patch prevents unrelated exceptions by the following rspec-core feature.
rspec/rspec-core#2568
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