Rails 6 and Rspec : How to test Zeitwerk mode

What is Zeitwerk mode?

Rails 6 introduced a new way of loading your application code, named Zeitwerk. It is the part of Rails that allows, for example, a model named User, defined in app/models/user.rb, to be used in other parts of the code without needing require Useron top of every other file.

# app/models/user.rb
class User < ApplicationRecord
end
end
# some other file
# no need for require User, it's already autoloaded
...
user = User.find(id)
...

With Zeitwerk, the way constants (Class and Modules) are autoloaded is somewhat optimized, or at least some of the pitfalls from the past are alleviated. The official documentation is quite detailed, and this article explains well the difference with the “classic” approach of previous Rails version.

The only important point to remember for this article is that Zeitwerk scans files at startup, and expect constants based on filenames : app/models/user.rbMUST defineUser.

Classic mode does the opposite : it lazyloads User , and when needed it looks for its code in app/models/user.rb.

Small difference, big consequences.

The Problem: how to test it

If you are managing an application in production, that a business relies upon, you probably have a solid test suite in place, hopefully with a good Continuous Integration system that allows you to push code to production without fear of breaking something. If you dont, you should — I honestly dont know how you can keep your sanity. At Wemind, thousands of members rely on us for insurance quotes and healthcare reimbursements, partners expect from us accurate data, and our internal tools must be completely reliable, so you can bet we have many, many tests covering almost our entire codebase.

And the first time we upgraded to Zeitwerk, the entire Wemind infrastructure went down crashing.

Unable to load application: Zeitwerk::NameError: expected file /app/app/jobs/obsolete_job.rb to define constant ObsoleteJob, but didn’t

The error was easy enough to spot and correct. The code in app/jobs/obsolete_job.rb had been commented out. Whereas Classic mode didnt bother since ObsoleteJob was never called, Zeitwerk scanned the file and expected it to define the constant.

The whole thing lasted only 15 minutes, but we are not used to downtime at Wemind. Why had it not been caught by tests ?

Turns out that, Test environment does NOT preload aggressively all the code, to speed things up. Therefore, it does NOT encounter the bug, which can then be pushed to Production and crash everything.

# config/environments/test.rb...
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that
# preloads Rails for running tests, you may have to set it to true.
config.eager_load = false
...

The solution

You can set config.eager_load = true. However, this will slow things down (I have not tested by how much. Perhaps it’s negligible?)

You can test Zeitwerk autoloading specifically by forcing one autoload. The test is super short to write and will catch production-breaking mistakes :

# not sure where to put it... spec/support/zeitwerk_spec.rb perhaps?require 'rails_helper'describe 'Zeitwerk' do
it 'eager loads all files' do
expect{ Zeitwerk::Loader.eager_load_all }.not_to raise_error
end
end

That’s it! Problem will not come back.

--

--

--

Helping freelancers as CTO of https://www.wemind.io, and letting teams connect with shared Slack channels at https://www.smooz.io

Love podcasts or audiobooks? Learn on the go with our new app.

Scrum vs. Kanban

Neumorphism Pagination Using HTML/CSS

Baby’s first programming language

6 Methods to Reduce cost of Mobile App Development

Playing local and remote media files on Android using ExoPlayer

Does business care about algorithmic skills?

SAP on Cloud IaaS Automation — Nov 2020

Front-end web development is not what you think it is.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matthieu Varagnat

Matthieu Varagnat

Helping freelancers as CTO of https://www.wemind.io, and letting teams connect with shared Slack channels at https://www.smooz.io

More from Medium

Algorithms In Context #9: Auto-Completion

Real-Time Operating System - A Dynamic Operating System

real time oprating system

Creating a Binary Search Tree, inserting and LeetCode 653

IO Streams are Dynamic!!