Module: RuboCop::RSpec::ExpectOffense
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Defined in: | lib/rubocop/rspec/expect_offense.rb |
Overview
Mixin for #expect_offense and #expect_no_offenses
This mixin makes it easier to specify strict offense expectations in a declarative and visual fashion. Just type out the code that should generate an offense, annotate code by writing '^'s underneath each character that should be highlighted, and follow the carets with a string (separated by a space) that is the message of the offense. You can include multiple offenses in one code snippet.
Autocorrection can be tested using #expect_correction after #expect_offense.
If you do not want to specify an offense then use the companion method #expect_no_offenses. This method is a much simpler assertion since it just inspects the source and checks that there were no offenses. The #expect_offense method has to do more work by parsing out lines that contain carets.
If the code produces an offense that could not be autocorrected, you can use #expect_no_corrections after #expect_offense.
If your code has variables of different lengths, you can use %{foo}
,
^{foo}
, and _{foo}
to format your template; you can also abbreviate
offense messages with […]
:
%w[raise fail].each do |keyword|
expect_offense(<<~RUBY, keyword: keyword)
%{keyword}(RuntimeError, msg)
^{keyword}^^^^^^^^^^^^^^^^^^^ Redundant {RuntimeError} argument [...]
RUBY
%w[has_one has_many].each do |type|
expect_offense(<<~RUBY, type: type)
class Book
%{type} :chapter, foreign_key: 'book_id'
_{type} ^^^^^^^^^^^^^^^^^^^^^^ Specifying the default [...]
end
RUBY
end
If you need to specify an offense on a blank line, use the empty ^{}
marker:
Instance Method Summary
-
#expect_correction(correction, loop: true, source: nil)
Metrics/MethodLength, Metrics/CyclomaticComplexity.
-
#expect_no_corrections
Metrics/MethodLength, Metrics/CyclomaticComplexity.
- #expect_no_offenses(source, file = nil)
- #expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
- #format_offense(source, **replacements)
- #parse_annotations(source, raise_error: true, **replacements)
- #parse_processed_source(source, file = nil)
- #set_formatter_options
Instance Method Details
#expect_correction(correction, loop: true, source: nil)
Metrics/MethodLength, Metrics/CyclomaticComplexity
# File 'lib/rubocop/rspec/expect_offense.rb', line 138
def expect_correction(correction, loop: true, source: nil) if source expected_annotations = parse_annotations(source, raise_error: false) @processed_source = parse_processed_source(expected_annotations.plain_source) _investigate(cop, @processed_source) end raise '`expect_correction` must follow `expect_offense`' unless @processed_source source = @processed_source.raw_source raise 'Use `expect_no_corrections` if the code will not change' if correction == source iteration = 0 new_source = loop do iteration += 1 corrected_source = @last_corrector.rewrite break corrected_source unless loop break corrected_source if @last_corrector.empty? if iteration > RuboCop::Runner::MAX_ITERATIONS raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [@offenses]) end # Prepare for next loop @processed_source = parse_source(corrected_source, @processed_source.path) _investigate(cop, @processed_source) end raise 'Expected correction but no corrections were made' if new_source == source expect(new_source).to eq(correction) expect(@processed_source).to be_valid_syntax, 'Expected correction to be valid syntax' end
#expect_no_corrections
Metrics/MethodLength, Metrics/CyclomaticComplexity
# File 'lib/rubocop/rspec/expect_offense.rb', line 176
def expect_no_corrections raise '`expect_no_corrections` must follow `expect_offense`' unless @processed_source return if @last_corrector.empty? # This is just here for a pretty diff if the source actually got changed new_source = @last_corrector.rewrite expect(new_source).to eq(@processed_source.buffer.source) # There is an infinite loop if a corrector is present that did not make # any changes. It will cause the same offense/correction on the next loop. raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [@offenses]) end
#expect_no_offenses(source, file = nil)
[ GitHub ]# File 'lib/rubocop/rspec/expect_offense.rb', line 190
def expect_no_offenses(source, file = nil) offenses = inspect_source(source, file) expected_annotations = AnnotatedSource.parse(source) actual_annotations = expected_annotations.with_offense_annotations(offenses) expect(actual_annotations.to_s).to eq(source) end
#expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
[ GitHub ]# File 'lib/rubocop/rspec/expect_offense.rb', line 115
def expect_offense(source, file = nil, severity: nil, chomp: false, **replacements) expected_annotations = parse_annotations(source, **replacements) source = expected_annotations.plain_source source = source.chomp if chomp @processed_source = parse_processed_source(source, file) @offenses = _investigate(cop, @processed_source) actual_annotations = expected_annotations.with_offense_annotations(@offenses) expect(actual_annotations).to eq(expected_annotations), '' expect(@offenses.map(&:severity).uniq).to eq([severity]) if severity # Validate that all offenses have a range that formatters can display expect do @offenses.each { |offense| offense.location.source_line } end.not_to raise_error, 'One of the offenses has a misconstructed range, for ' \ 'example if the offense is on line 1 and the source is empty' @offenses end
#format_offense(source, **replacements)
[ GitHub ]#parse_annotations(source, raise_error: true, **replacements)
[ GitHub ]# File 'lib/rubocop/rspec/expect_offense.rb', line 198
def parse_annotations(source, raise_error: true, **replacements) source = format_offense(source, **replacements) annotations = AnnotatedSource.parse(source) return annotations unless raise_error && annotations.plain_source == source raise 'Use `expect_no_offenses` to assert that no offenses are found' end
#parse_processed_source(source, file = nil)
[ GitHub ]# File 'lib/rubocop/rspec/expect_offense.rb', line 208
def parse_processed_source(source, file = nil) processed_source = parse_source(source, file) return processed_source if processed_source.valid_syntax? raise 'Error parsing example code: ' \ "#{processed_source.diagnostics.map(&:render).join("\n")}" end
#set_formatter_options
[ GitHub ]# File 'lib/rubocop/rspec/expect_offense.rb', line 216
def RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {} RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {} cop.instance_variable_get(:@options)[:autocorrect] = true end