DEV Community

loading...
Cover image for Testing Rails 6 Zeitwerk Autoloading

Testing Rails 6 Zeitwerk Autoloading

marcqualie profile image Marc Qualie Originally published at marcqualie.com ・2 min read

Rails 6 introduced a new autoloader called Zeitwerk. While it has many many improvements, one of the main changes is how strict it is with filenames and the constants they define. Unless you have full coverage on your application (few apps actually reach 100%) it can be quite easy to end up with an app that won't boot in your staging/production environment.

This is a welcome change from me, I believe it leads to much more stable and predictable codebases when the filenames and constants match 1:1. This can become an issue for anyone used to working with the rails classic autoloader since that allowed developers to get away with mostly anything inside their files.

Adding the following spec to any new rails 6+ project will protect from such problems, as the autoloading will be triggered in the CI environment instead; before merge and deploy. This is also perfect if you're upgrading any older Rails 5 apps and you're nervous about switching to the new autoloader.

# spec/zeitwerk_spec.rb
require 'rails_helper'

describe 'Zeitwerk' do
  it 'eager loads all files' do
    expect do
      Zeitwerk::Loader.eager_load_all
    end.to_not raise_error
  end
end
Enter fullscreen mode Exit fullscreen mode

Original reference post for code snippet Rails 6 and Rspec : How to test Zeitwerk mode by Matthieu Varagnat

You can take this a step further on CI and have a dedicated job that runs specifically to check the autoloading. This can be dne before the tests (to avoid wasted minutes), or in parallel to highlight an autoloading issues in your PR status checks.

The following line can run in any shell to perform the same job as the rspec snippet above:

rails r 'Zeitwerk::Loader.eager_load_all'
Enter fullscreen mode Exit fullscreen mode

Here is an example of my CircleCI flow for a recently upgraded rails project. Notice how Zeitwerk failure prevented a ton of other jobs triggering, which would have used quite a lot of resources and failed much later.

Screen-Shot-2021-01-20-14-02-56.26.png

Screen-Shot-2021-01-20-14-05-09.95.png

This, of course, does not replace the need for an extensive test suite, but it can save you in the early days of prototype development where a typo or unused class can slip into the codebase when working at a fast pace.

Discussion (2)

pic
Editor guide
Collapse
janko profile image
Janko Marohnić

I ran into a similar problem, and I ended up enabling eager loading when tests are running on CI:

# config/environments/test.rb
Rails.application.configure do
  # ...
  config.eager_load = ENV["CI"] ? true : false
  # ...
end
Enter fullscreen mode Exit fullscreen mode
Collapse
marcqualie profile image
Marc Qualie Author

Good shout! This is a great way for getting this behaviour without any CI changes at all.

The only downside to this is that it's hard to see what the actual issue is without going into the sea of red rspec logs. I prefer to have one CI job per specific thing that's being built/tested, but it's a bit more work to configure.

Configuring this in a chained workflow also means other jobs (like rspec, webpacker etc) can be prevented from even starting to execute, rather than each one being setup in parallel and all failing with the same error.