rspec-mocks: 'not defined constant' errors when using instance_double
I have the following:
let(:project) { instance_double 'Project' }
But when I run specs that access this double, I get Project is not a defined constant. Perhaps you misspelt it?
I have the following in my spec_helper.rb file:
config.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true
end
I have a Project class with:
class Project < ActiveRecord::Base
What am I missing here?
About this issue
- Original URL
- State: closed
- Created 10 years ago
- Comments: 28 (18 by maintainers)
At the time your spec runs,
Projectis not a defined constant. Are you loading it anywhere (e.g. withrequire 'project'or similar)?If you are relying upon rails autoloading, you can change
instance_double 'Project'toinstance_double Project(it works fine to pass the class itself rather than the string) – referencing the constant will cause rails to load the model.In general, if you always want your verifying doubles to verify and are willing to pay the cost of loading the classes, passing the class as a constant reference works simpler/better than using the string form and
verify_doubled_constant_names = true. OTOH, using the string form provides a way to write an isolated spec that doesn’t require the named class to be loaded but will verify when it is loaded. When doing that, I tend to setverify_doubled_constant_names = truein a config file that is only loaded when all specs are run (and all implementation files are loaded). For individual spec files, usingverify_doubled_constant_names = truekinda destroys the whole point of passing the class name as a string, as it prevents you from being able to run the spec w/o the class loaded, which is the entire point of supporting the string form in the first place.Closing, but let me know if that doesn’t make sense and we can reopen.
In an imaginary ideal world, what I want is for these features and their benefits to just all work as advertised in a Rails app. And a pony.
That would mean:
spec_helper.rb) quickly in isolation without having to load the whole app, but when I run all the integration/acceptance tests as well (rails_helper.rb) I get failures if I’ve accidentally stubbed methods that don’t exist. There is a smooth continuum here: if I run a subset of unit tests such that some tests happen to load classes that other tests reference with verifying doubles, I get a bit of checking.verify_doubled_constant_names. This means I see failures if I accidentally make a typo in the strings I use to create verifying doubles, whenever that is possible to detect.I understand the tension here, since it’s hard to tell the difference between “this class doesn’t exist” and “this class hasn’t been autoloaded yet” in a Rails app. But at a minimum it would be helpful to have some documentation that says: we recommend you do such-and-such if you’re using Rails. (There are a few candidates in this thread already, but those are just GitHub comments, not official advice.) If that recommendation is to have a special separate Rake task for preloading everything and running with
verify_doubled_constant_namesbecause autoloading and name verification don’t mix, or even to not use name verification at all with Rails, that’s absolutely fine. Just say so.A more ideal solution in principle (although perhaps impossible in practice) would be for name verification to decide whether a particular constant name looks plausible for the current Rails app, i.e. does there exist a file whose name and path would cause Rails’ autoloader to try to define this constant if we referenced it? That would preserve the benefits of “we don’t have to load everything” but without the downside of “if we make a typo in a name string, we may literally never be told about it”.
In any event, the eventual solution must account for the realities of Rails. Rails projects are not like plain Ruby projects. Rails applications cannot have all of their dependencies loaded with
require 'project'. Most Rails devs don’t even know how autoloading works, so they need specific documentation (if not automatic implementation) of “just require all your files”; Rails does not do this for you in thetestenvironment.