Troubleshooting LoadErrors in Rails tests

  • Posted By Stuart Halloway on February 08, 2008

I am proposing a patch to help cope with the dreaded Rails LoadError:

LoadError: Expected foo.rb to define Foo

In Ruby, it is simple to load code, just require it. In script/console:

>> require 'account_controller'
=> ["AccountController"]

Rails extends this to magically find classes just based on their name:

>> AccountController
=> AccountController

Most of the time, if a class does not exist, you get a helpful exception:

>> AccountController
MissingSourceFile: no such file to load -- hpricot_scan

Ah, so my AccountController depends on hpricot, which isn't available for some reason. Solution: go find hpricot.

But once in a while this problem presents a different symptom:

>> AccountController
LoadError: Expected account_controller.rb to define AccountController

This is confusing, since account_controller.rb does define AccountController! Experienced Rails developers know that this cryptic message actually means "Something went wrong in application_controller.rb, but Rails swallowed the real exception." After being bitten by this on three different projects in the last two weeks, I decided to track the issue down. Turns out the problem is in how fixtures get loaded:

begin
  require_dependency file_name
rescue LoadError
  # Let's hope the developer has included it himself
end

After fixtures swallow the real MissingSourceFile for a subdependency such as hpricot, ActiveSupport raises a misleading LoadError for the original dependency (account_controller.rb) that references hpricot.

In a perfect world, I would simply have fixtures stop swallowing LoadErrors. But the comment strongly suggests that some code depends on this behavior. So weaker sauce is to at least log the problem:

def try_to_load_dependency(file_name)
  require_dependency file_name
rescue LoadError => e
  ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
end

If anybody knows how to distinguish the confusing LoadErrors from the expected ones, please go and improve the patch.