Ruby on Rails / July 3, 2025 / 4 mins read / By Wagner Matos

Rails Testing Gotcha: When Concerns Work in Console But Not in Tests

If you’ve ever encountered a situation where your Rails model associations work perfectly in the console but mysteriously fail in your test suite, you’re not alone. I recently ran into this exact issue and wanted to share the solution along with some insights about Rails’ autoloading behavior.

The Problem

I was working with a Rails app that uses concerns to share common functionality across models. One of these concerns, Addressable, defines a polymorphic association:

module Addressable
  extend ActiveSupport::Concern

  included do
    has_many :addresses, as: :addressable, dependent: :destroy
    accepts_nested_attributes_for :addresses, allow_destroy: true, reject_if: :all_blank
  end

  def full_address
    address = addresses.last
    return "No address available" if address.nil?
    # ... rest of method
  end
end

My Estate model includes this concern:

class Estate < ApplicationRecord
  include BelongsToOrganisation
  include Addressable
  include ActiveAccountingPeriod

  has_many :blocks
  has_many :properties, through: :blocks
  # ... other associations
end

Everything worked beautifully in the Rails console. I could create estates, add addresses, and call all the methods from the concern without any issues.
But then my FactoryBot tests started failing:

# spec/factories/estates.rb
FactoryBot.define do
  factory :estate do
    sequence(:name) { |n| "Estate #{n}" }
    association :organisation

    after(:create) do |estate|
      estate.addresses << build(:address, addressable: estate)
    end
  end
end

The error was confusing:

NoMethodError: undefined method 'addresses' for an instance of Estate

The Investigation

What made this particularly puzzling was that the concern seemed to be loaded properly. When I added debug output to check the model’s ancestors, I could see Addressable was included:

puts estate.class.ancestors
# => [Estate, ActiveAccountingPeriod, Addressable, BelongsToOrganisation, ...]

But when I checked the available associations:

puts estate.class.reflect_on_all_associations.map(&:name)
# => [:pay_customers, :charges, :subscriptions, :blocks, :properties, :expense_categories, :transactions, :accounting_periods]

Notice what’s missing? The addresses association from the Addressable concern wasn’t there.

The Root Cause

The issue stems from Rails’ different autoloading behavior between development and test environments:

Development Environment:

  • Rails uses lazy loading
  • When you reference Estate in the console, Rails loads the model and properly includes all concerns
  • The included block in concerns executes when the model is first loaded

Test Environment:

  • Rails optimizes for fast test startup
  • Models and concerns aren’t always loaded in the same order or manner
  • The included block in concerns might not execute before your tests run

The Solution

The fix is to explicitly require your concerns and models in your test setup. Add this to your rails_helper.rb:

# spec/rails_helper.rb
Dir[Rails.root.join('app/models/concerns/*.rb')].each { |f| require f }
Dir[Rails.root.join('app/models/*.rb')].each { |f| require f }

This ensures that all your models and their concerns are loaded before any tests run, making the test environment behave consistently with development.

Alternative Approaches

There are a few other ways to solve this issue:

1. Eager Load Everything

# In rails_helper.rb
RSpec.configure do |config|
  config.before(:suite) do
    Rails.application.eager_load!
  end
end

This loads your entire application, which is more comprehensive but potentially slower.

2. Adjust Test Environment Configuration

# config/environments/test.rb
config.eager_load = true

Though this changes the fundamental behavior of your test environment.

3. Force Model Loading in Factories

# At the top of your factory file
Estate  # Forces the model to load

FactoryBot.define do
  factory :estate do
    # ... factory definition
  end
end

Why This Matters

This issue highlights an important aspect of Rails development: the framework’s autoloading behavior can vary between environments. While this usually works seamlessly, it can occasionally lead to subtle bugs that are hard to track down.

The explicit require approach I used is targeted and fast—it only loads what you need rather than the entire application. It’s also explicit about the dependency, making it clear that your tests rely on these files being loaded.

Takeaways

  • Environment differences matter: Always test your code in the same environment where it will run in production
  • Explicit is better than implicit: When in doubt, explicitly require the files you need
  • Debug systematically: Use Rails’ reflection methods to understand what’s actually loaded
  • Consider autoloading: Be aware of how Rails loads your code in different environments

Have you encountered similar issues with concerns and testing? I’d love to hear about your experiences and solutions in the comments below.

This post originally started as a question to the Rails community on Discord and Twitter. Thanks to everyone who shared their insights and experiences!