admin管理员组

文章数量:1277910

Just upgrading a Rails 7.2.2 app to Rails 8.0.1. Before the upgrade, all tests run cleanly. App was upgraded to ruby 3.4.2 just before (and all tests passed with no warnings). After upgrade, this warning is emitted:

/home/<username>/.asdf/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-8.0.1/lib/rails/tasks/statistics.rake:4: warning: already initialized constant STATS_DIRECTORIES Time: 00:00:10,  ETA: 00:03:50
/home/<username>/.asdf/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-8.0.1/lib/rails/tasks/statistics.rake:4: warning: previous definition of STATS_DIRECTORIES was here

I have traced the issue to one test suite for a Rake Task that is run regularly in production by Heroku Scheduler.

The test looks like:

require "test_helper"

class TimedEmailsTaskTest < ActionDispatch::IntegrationTest
  setup do
    Rails.application.load_tasks

    @my_object = create(:object)
  end

  teardown do
    ActionMailer::Base.deliveries.clear
    Rake::Task["my_regular_emails_task"].reenable
  end

  ... 4 tests that assert things using Rake::Task["my_regular_emails_task"].invoke ...

end

The offending line is Rails.application.load_tasks, which I tracked down with the help of this github issue. Originally the warning was emitted once for each test in this suite; I can get it down to once by using:

    Rails.application.load_tasks unless defined?(Rake::Task) && Rake::Task.tasks.any?

With the help of some good old puts debugging, I have tracked the initial "hit" on statistics.rake:4 to my Rakefile (in the app root directory), which is the standard default file:

require_relative "config/application"

Rails.application.load_tasks

So, the Rakefile runs, sets the constant STATS_DIRECTORIES, but the tasks aren't available in tests without running Rails.application.load_tasks again, which emits the warning. Interestingly, If I just run the single test file, the Rakefile doesn't run and warning isn't emitted; it only happens when running multiple tests (e.g. rails test or rails test:integration).

I have spent a few hours with this, including trying to move statements to test_helper.rb with no meaningful change in the outcome. I'm also unhappy with the possibility of creating flaky tests that either emit a warning when running the suite and pass silently when running the one file, or fail when running the one file and pass (with warning) when running the full suite. I'm somewhat confused about how Rakefile works and when it runs, and confused why this wasn't an issue on Rails 7. Is there an insight I'm missing? Is there an obvious misconfiguration here or more idiomatic approach that would solve this?

PS: This same railties line was emitting warnings for people back on Rails 4, but that question was never resolved so isn't much help.

Just upgrading a Rails 7.2.2 app to Rails 8.0.1. Before the upgrade, all tests run cleanly. App was upgraded to ruby 3.4.2 just before (and all tests passed with no warnings). After upgrade, this warning is emitted:

/home/<username>/.asdf/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-8.0.1/lib/rails/tasks/statistics.rake:4: warning: already initialized constant STATS_DIRECTORIES Time: 00:00:10,  ETA: 00:03:50
/home/<username>/.asdf/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/railties-8.0.1/lib/rails/tasks/statistics.rake:4: warning: previous definition of STATS_DIRECTORIES was here

I have traced the issue to one test suite for a Rake Task that is run regularly in production by Heroku Scheduler.

The test looks like:

require "test_helper"

class TimedEmailsTaskTest < ActionDispatch::IntegrationTest
  setup do
    Rails.application.load_tasks

    @my_object = create(:object)
  end

  teardown do
    ActionMailer::Base.deliveries.clear
    Rake::Task["my_regular_emails_task"].reenable
  end

  ... 4 tests that assert things using Rake::Task["my_regular_emails_task"].invoke ...

end

The offending line is Rails.application.load_tasks, which I tracked down with the help of this github issue. Originally the warning was emitted once for each test in this suite; I can get it down to once by using:

    Rails.application.load_tasks unless defined?(Rake::Task) && Rake::Task.tasks.any?

With the help of some good old puts debugging, I have tracked the initial "hit" on statistics.rake:4 to my Rakefile (in the app root directory), which is the standard default file:

require_relative "config/application"

Rails.application.load_tasks

So, the Rakefile runs, sets the constant STATS_DIRECTORIES, but the tasks aren't available in tests without running Rails.application.load_tasks again, which emits the warning. Interestingly, If I just run the single test file, the Rakefile doesn't run and warning isn't emitted; it only happens when running multiple tests (e.g. rails test or rails test:integration).

I have spent a few hours with this, including trying to move statements to test_helper.rb with no meaningful change in the outcome. I'm also unhappy with the possibility of creating flaky tests that either emit a warning when running the suite and pass silently when running the one file, or fail when running the one file and pass (with warning) when running the full suite. I'm somewhat confused about how Rakefile works and when it runs, and confused why this wasn't an issue on Rails 7. Is there an insight I'm missing? Is there an obvious misconfiguration here or more idiomatic approach that would solve this?

PS: This same railties line was emitting warnings for people back on Rails 4, but that question was never resolved so isn't much help.

Share Improve this question edited Feb 24 at 1:53 NGobin asked Feb 24 at 1:46 NGobinNGobin 4034 silver badges17 bronze badges 2
  • Did you try removing Rails.application.load_tasks from your test? Is the test still passing? – Greg Commented Feb 25 at 5:48
  • Yes, I tried that. Removing Rails.application.load_tasks from the test causes the test to fail, and without it Rake::Task.tasks.any? in the test suite returns False. Commenting the load_tasks line in the Rakefile causes the warning not to be emitted (because it only runs once). It seems like the Rakefile runs and loads the tasks, but then they're cleared before the test suite starts (puts Rake::Task.tasks.any? in the Rakefile returns True). – NGobin Commented Feb 26 at 20:48
Add a comment  | 

1 Answer 1

Reset to default 0

I've gotten around this for now by implementing the guidance on this reddit thread. Basically, I refactored all the logic from the task into a service object, and then the Rake task becomes a single line call that I leave untested. It's probably a better architectural solution, but my OCD doesn't like leaving the task untested, even if it's just a single line, because it's production critical. Oh well.

It still doesn't really answer when/why/how the rakefile runs when running the test suite, but not a single file of tests, why the rake tasks loaded in the rakefile don't persist and need to be re-loaded (but apparently constant definitions do persist), how to load a task in tests without incurring this warning, and why this popped up in Rails 8.0 but was silent in Rails 7.

Anyways, I'll leave this answer as not accepted for a good long time in case someone else has insight on the core questions.

本文标签: