-
-
Notifications
You must be signed in to change notification settings - Fork 356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
'not defined constant' errors when using instance_double #842
Comments
At the time your spec runs, If you are relying upon rails autoloading, you can change 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 Closing, but let me know if that doesn't make sense and we can reopen. |
@myronmarston - Thanks for the explanation. But when I use this: let(:project) { instance_double(Project, metric?: false) } I now get |
@myronmarston - please disregard. I figured it out. Thanks again. |
@myronmarston would it make sense for RSpec to anticipate autoloading and try to resolve it? It seems like it could be something like: begin
const_get(class_name)
rescue NameError
# show error message
end |
@mockdeep not really because that behaviour isn't prevalent outside of Rails, it's much more common to have to require the things you expect to use. If you're using rspec in rails environment using the constant directly is a much better way to force the desired behaviour. |
@JonRowe I guess that makes sense, though it seems like it would make the implementation a little more flexible for these kinds of use cases (as well as less confusing). I'll use the constant for now. |
@mockdeep - if you are wanting/expecting the class to be loaded, why use the string form at all? The entire reason the string form exists is for situations where the user doesn't want the class loaded, so adding logic to it to load the const goes against its entire purpose. |
@myronmarston So I can think of two arguments in favor of a more flexible implementation. The first is surprise. Various examples on the internet (including Avdi's video) use the string syntax, so when someone tries to emulate that and it doesn't work for them, they're going to have to google around to figure out what happened, or they'll open an issue on RSpec, or maybe they'll just give up. There's this subtle caveat that if you're in Rails or another auto-loaded environment, you have to use this other syntax in order to get it to work. The other point is that one might want to be able to toggle the verification on and off. I could see a use case that while you're driving something out you aren't necessarily going to be worried that the interfaces are spot on, so you would be inclined to use the string syntax, but once the individual components are in place you could toggle on the verification. Right now it sort of pushes a "depth first" approach to testing in a Rails environment, as you have to define each of the classes you're mocking out down the tree. I personally don't mind, but I could see it being a little frustrating at times. |
You forget that Rails is causing the surprising case here, and even then only without eager_loading turned on, in normal Ruby the string usage behaves as expected, you'd have to require the class before use. |
That's true, I'm not blaming RSpec for the confusion necessarily, but it's there. @myronmarston explained it in the comments for the video, but |
Rails autoloading has more confusing behaviour than just this. If you expect something to be present you should load the entire of app or specify the bits you do want. |
How so? I haven't run into problems with it in my tests, aside from making sure all of my folders are included in the autoload paths. I've never had to manually require individual files for it. |
It has some quirks regarding name spaced constants and the like |
I'm thinking we should update our docs to show the non-string form and explain when you'd want to use one vs the other.
What? I admittedly haven't done a real rails project since the rails 2 days, but I find this surprising. I believe @xaviershay (the designer of verifying doubles) tends to use the string form in rails projects without any issues. What makes you say you have to use the non-string syntax to get it to work? Is it just that RSpec doesn't load the constant for you? (if so, that's it working as designed).
This is what the string form is meant to provide -- you can use the double before the interface is defined, and later, once it is defined, when your test that uses the verified double runs with the class definition loaded, the verification will be enabled. |
So I think we might be talking about different scenarios here. I'm not sure how @xaviershay handles it, of course, but there is the situation that is automatically provided, and then the more strict configuration Setting the stricter In general, I think it makes sense to implement Rails specific adjustments in begin
klass = const_get(class_name)
rescue NameError
# if `mocks.verify_doubled_constant_names = true`
# make noise
# else
# don't worry about it
end |
Yep, I think we are talking about different situations here. I didn't realize you were talking about
Something else in your environment should be loading or referencing the class constant though, right? If you're depending upon RSpec to load the constant than how would it ever get loaded in dev and prod? (As an aside, this is one reason I dislike rails autoloading: there's a lot of uncertainty about whether or not a specific thing is loaded or not). Regardless, now that I know you're specifically talking about when |
In development and test the classes are loaded on demand when they are referenced. In production Rails goes through the auto-load paths and requires everything at boot time. In the case of tests where you're mocking out the class under question, it wouldn't necessarily be loaded, so it makes sense to me to have RSpec try to reference it and respond appropriately if it doesn't pop up. It's a semantic difference, but I wouldn't see it so much as getting "RSpec to load the constant" so much as having RSpec look for it in a way that would trigger
It can be confusing at times, though I honestly haven't run up against it too much. It certainly seems better than the alternative for large projects. In smaller projects/gems it clearly makes sense to just use manual |
So turns out I don't actually use My second reaction was that the test environment should eager load everything, though this doesn't appear to be the rails default. Let me try setting it on a few projects and then I'll have some opinions.
this times one thousand, but it's an academic point ;) |
@xaviershay Yeah, not too sure what the implications of eager loading in test environment are. I don't see any performance differences, but I seem to remember some odd behavior around it with gems like |
Yep, I was indeed the source of the feature :). Unfortunately, using that feature well is a bit tricky. You don't want to always turn it on, as doing so totally defeats the purpose of supporting the string form of instance doubles. It's a good option to have on on CI and when running your entire spec suite, though. Here's my usual habit for it:
|
Turns out I do use eager loading, just missed it the first time I looked. I have a file
I then can use it like I don't use |
That would certainly do the trick. Still, I think it's suboptimal for users to have to set that up. If they've configured @JonRowe, could the verifying doubles callback you're working with be used in rspec-rails to try to force the constant to load if |
Got bitten by this today. Is it likely to be fixed automatically in upcoming |
@tomstuart: it depends on how you want to use verifying doubles. I'm not sure that rspec-rails can fix this in a way that pleases everyone. If you want the verifying double to always get verified (which requires the constant to be loaded, but has the downside of adding the load time of the desired class and any of its dependencies), my suggestion is to not use the string form of OTOH, if you want to be able to use the verifying double in both modes (e.g. w/o the constant loaded and w/o verification vs w/ the constant loaded and w/ verification), then you'll have to use the string form ( RSpec.configure do |c|
c.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true
end
end
# load all doubled constants here, either by putting explicit requires
# or by looping over the appropriate directories and loading all ruby
# files in them.
require 'project' Then you could configure things so this file is only loaded in particular situations (e.g. as part of your CI builds). Would either of those options work for you? If not, we'll need more information about how you want to use verifying doubles to advise. |
I think we should try to load the constant (when |
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:
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 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 |
To further clarify: the dream is to be told "you made a typo" without having to load the class, since in many cases the class can't be successfully loaded without doing a bunch of other setup (i.e. bootstrapping the Rails app). I accept that this may well not be possible. |
Given how Rails conventions work, I'd like it to be possible to ask Rails "what is the file name convention for this constant" and then say "does that file exist in the current autoload paths" and treat that as a best guess for "does this exist" |
I have the following:
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:
I have a Project class with:
What am I missing here?
The text was updated successfully, but these errors were encountered: