lib/rubocop/cop/rspec/instance_variable.rb in rubocop-rspec-1.6.0 vs lib/rubocop/cop/rspec/instance_variable.rb in rubocop-rspec-1.7.0
- old
+ new
@@ -1,13 +1,17 @@
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
- # When you have to assign a variable instead of using an instance
- # variable, use let.
+ # Checks for instance variable usage in specs.
#
+ # This cop can be configured with the option `AssignmentOnly` which
+ # will configure the cop to only register offenses on instance
+ # variable usage if the instance variable is also assigned within
+ # the spec
+ #
# @example
# # bad
# describe MyClass do
# before { @foo = [] }
# it { expect(@foo).to be_empty }
@@ -16,25 +20,62 @@
# # good
# describe MyClass do
# let(:foo) { [] }
# it { expect(foo).to be_empty }
# end
+ #
+ # @example with AssignmentOnly configuration
+ #
+ # # rubocop.yml
+ # RSpec/InstanceVariable:
+ # AssignmentOnly: false
+ #
+ # # bad
+ # describe MyClass do
+ # before { @foo = [] }
+ # it { expect(@foo).to be_empty }
+ # end
+ #
+ # # allowed
+ # describe MyClass do
+ # it { expect(@foo).to be_empty }
+ # end
+ #
+ # # good
+ # describe MyClass do
+ # let(:foo) { [] }
+ # it { expect(foo).to be_empty }
+ # end
+ #
class InstanceVariable < Cop
+ include RuboCop::RSpec::SpecOnly, RuboCop::RSpec::Language
+
MESSAGE = 'Use `let` instead of an instance variable'.freeze
- EXAMPLE_GROUP_METHODS = [
- :example_group, :describe, :context, :xdescribe, :xcontext,
- :fdescribe, :fcontext, :shared_examples, :shared_context,
- :share_examples_for, :shared_examples_for, :feature
- ].freeze
+ EXAMPLE_GROUP_METHODS = ExampleGroups::ALL + SharedGroups::ALL
+
+ def_node_matcher :spec_group?, <<-PATTERN
+ (block (send _ {#{EXAMPLE_GROUP_METHODS.to_node_pattern}} ...) ...)
+ PATTERN
+
+ def_node_search :ivar_usage, '$(ivar $_)'
+
+ def_node_search :ivar_assigned?, '(ivasgn % ...)'
+
def on_block(node)
- method, _args, _body = *node
- _receiver, method_name, _object = *method
- @in_spec = true if EXAMPLE_GROUP_METHODS.include?(method_name)
+ return unless spec_group?(node)
+
+ ivar_usage(node) do |ivar, name|
+ return if assignment_only? && !ivar_assigned?(node, name)
+
+ add_offense(ivar, :expression, MESSAGE)
+ end
end
- def on_ivar(node)
- add_offense(node, :expression, MESSAGE) if @in_spec
+ private
+
+ def assignment_only?
+ cop_config['AssignmentOnly']
end
end
end
end
end