everydayrails / rails-4-1-rspec-3-0

Code samples for Everyday Rails Testing with RSpec, Rails 4.1/RSpec 3.0 edition
272 stars 229 forks source link

Automatically require rails_helper in all specs #36

Open chrisvfritz opened 9 years ago

chrisvfritz commented 9 years ago

Notice the require 'rails_helper' at the top, and get used to typing it–all of your specs will include this line moving forward.

I actually like to add --require rails_helper to a .rspec file in my project root. This has the effect that all my specs are automatically run with rails_helper pre-required. You can do this with other parameters you'd like to use when running rspec as well. My full .rspec file reads:

--color
--format documentation
--require rails_helper
ruralocity commented 9 years ago

Thanks, clever idea. Maybe I should collect ideas like this in an appendix at some point.

cupakromer commented 9 years ago

I actually like to add --require rails_helper to a .rspec file in my project root.

It's fine to choose to do this. Just be aware that there is a reason why the default .rspec file generated by rspec-rails has --require spec_helper instead.

With RSpec 3, the general advise is that the spec/spec_helper.rb should be kept as light weight as possible. It is suggested that it only contain RSpec configuration settings and customizations. It is suggested to have additional setup (helper) files which can be required that are more heavy weight when necessary. One such file is the spec/rails_helper.rb.

The benefit here is that I only need to load Rails if / when I need it. For example, say I have an external service wrapper in my app. This object is not coupled to Rails in any way. When I build out this object I can run its spec in isolation without constantly paying the Rails startup tax. This same logic can be applied to any other heavy dependency your app may need.

chrisvfritz commented 9 years ago

Good point @cupakromer. As a project gets more complex, I'll typically extract these out into separate gems with their own specs, leaving very few specs (if any) where I don't need Rails.

An alternative strategy I might use in your case, assuming you're using the guard gem or something similar to automatically run specs as you work, would be to change --require 'rails_helper' in .rspec to --require 'spec_helper' (and remove require 'spec_helper' from rails_helper.rb), then manage dependency groups like this in my Guardfile:

guard :rspec, cmd: 'spring rspec --require rails_helper' do
  # spec paths that require 'rails_helper.rb'
end

guard :rspec, cmd: 'spring rspec --require extensions_helper' do
  # spec paths that require 'extensions_helper.rb'
end

guard :rspec, cmd: 'spring rspec' do
  # spec paths with no requirements other than what's in spec_helper.rb
end

This way way, I'm much leaner when running individual specs and when I want to run the entire suite, I can run rspec with something like this:

bundle exec rspec --require meta_helper

In this case, meta_helper would be a file that requires all the other helpers, to ensure all dependencies are met for every spec.

Thoughts? Is there a simpler solution that isn't coming to my mind right now? I really like to avoid all those require lines at the top of spec files if I can. :-)

cupakromer commented 9 years ago

I'll typically extract these out into separate gems with their own specs, leaving very few specs (if any) where I don't need Rails.

Yep, @chrisvfritz that's a completely valid point :heart:. I agree with you that in those cases it's a good time to consider your suggestion. To be clear, I wasn't trying to say to never do what you suggested. Just that people should understand the trade offs of the decision.

Personally in my recent projects, I can (and eventually do) move any code in lib/ into a gem. Yay for small, res-usable, code modules! However, I often still have code that is project specific, which lives in app/models. These domain models usually have zero Rails dependencies. For those I'd still rather not load Rails when testing in isolation

manage dependency groups like this in my Guardfile

I don't use guard, zeus, or any of the other preloads. The one exception I make currently is spring; but only because it is now bundled with Rails. I typically use editor plugins to run a single spec file, a subset of specs, a single spec, or the entire suite via hotkeys/shortcuts.

Something else to be aware of with this type of setup is that spec types are bound to directory paths. It's true that rspec-rails 2.x forced this. rspec-rails 3.x is moving away from this, allowing users to be more flexible in where they put different spec types. While I still generally follow the same layout of where specs live based on general "type", it's not always true they have the same core dependencies; such as in the case of the domain models (all app/models have specs in spec/models regardless of if they are type: :model or not).

Please understand I'm not saying I disagree with the use of guard, zeus, etc. My personal style just does not mesh well with how they work. I often save my files frequently, leaving invalid Ruby syntax, incomplete thoughts, etc. This causes frequent spec runs which fail, as I expect, but it generates too much noise for my personal taste. Sadly :crying_cat_face:, I've also had multiple bad experiences with those gems where specs get run multiple times or are never run. This causes me to spend more time debugging the setup then actually working on the code. Again, just my personal experience. If things work for you :+1: keep using them! :smile_cat:

Thoughts? Is there a simpler solution that isn't coming to my mind right now? I really like to avoid all those require lines at the top of spec files if I can. :-)

I can't think of a simpler solution. I have no problems with multiple require statements in my files.

Seeing the require lines helps tell me where to look for dependencies. It also serves as a rough metric for possible complexity of a spec (or associated object). For the most part, I try to keep each file that is required as lean and targeted to a single responsibility as possible. This means if I see more than four or five requires in a spec file, it may be time to take a closer look at the design.

I should note that my spec/rails_helper.rb file is generally only the default one generated. In fact, in a few projects which do not use Active Record it literally only loads the Rails environment and rspec-rails.

There are a few cases where do require a spec support file that has multiple sub-responsibilities. For example a general "top level" custom matcher file. However, in those instances I try to keep all dependent requires in one file and use autoload to keep load times down. For a real world example of this see: rspec/matchers.rb and rspec/matchers/built_in.rb

These days Ruby is good at handling seeing the same require in different files. It essentially becomes a no-op in subsequent files. So I don't really make any attempt to optimize that out.

chrisvfritz commented 9 years ago

All great points! Great conversation. :smile:

cupakromer commented 9 years ago

Yes, great convo! :smile: I should note that I do use bin/rspec --require rails_helper when I run on CI. I also have a local alias bspecr for that, when I want to run the full suite locally. :heart: