Module: RuboCop::RSpec::ExpectOffense

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 a 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.

Auto-correction 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 auto-corrected, you can use `expect_no_corrections` after `expect_offense`.

Examples:

Usage


expect_offense(<<~RUBY)
  a do
    b
  end.c
  ^^^^^ Avoid chaining a method call on a do...end block.
RUBY

Equivalent assertion without `expect_offense`


inspect_source(<<~RUBY)
  a do
    b
  end.c
RUBY

expect(cop.offenses.size).to be(1)

offense = cop.offenses.first
expect(offense.line).to be(3)
expect(offense.column_range).to be(0...5)
expect(offense.message).to eql(
  'Avoid chaining a method call on a do...end block.'
)

`expect_offense` and `expect_correction`


expect_offense(<<~RUBY)
  x % 2 == 0
  ^^^^^^^^^^ Replace with `Integer#even?`.
RUBY

expect_correction(<<~RUBY)
  x.even?
RUBY

`expect_offense` and `expect_no_corrections`


expect_offense(<<~RUBY)
  a do
    b
  end.c
  ^^^^^ Avoid chaining a method call on a do...end block.
RUBY

expect_no_corrections

Defined Under Namespace

Classes: AnnotatedSource

Instance Method Summary collapse

Instance Method Details

#expect_correction(correction) ⇒ Object

rubocop:enable Metrics/AbcSize, Metrics/MethodLength


103
104
105
106
107
108
109
110
111
112
113
# File 'lib/rubocop/rspec/expect_offense.rb', line 103

def expect_correction(correction)
  unless @processed_source
    raise '`expect_correction` must follow `expect_offense`'
  end

  corrector =
    RuboCop::Cop::Corrector.new(@processed_source.buffer, cop.corrections)
  new_source = corrector.rewrite

  expect(new_source).to eq(correction)
end

#expect_no_correctionsObject


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/rubocop/rspec/expect_offense.rb', line 115

def expect_no_corrections
  unless @processed_source
    raise '`expect_no_corrections` must follow `expect_offense`'
  end

  return if cop.corrections.empty?

  # In order to print a nice diff, e.g. what source got corrected to,
  # we need to run the actual corrections

  corrector =
    RuboCop::Cop::Corrector.new(@processed_source.buffer, cop.corrections)
  new_source = corrector.rewrite

  expect(new_source).to eq(@processed_source.buffer.source)
end

#expect_no_offenses(source, file = nil) ⇒ Object


132
133
134
135
136
137
138
139
# File 'lib/rubocop/rspec/expect_offense.rb', line 132

def expect_no_offenses(source, file = nil)
  inspect_source(source, file)

  expected_annotations = AnnotatedSource.parse(source)
  actual_annotations =
    expected_annotations.with_offense_annotations(cop.offenses)
  expect(actual_annotations.to_s).to eq(source)
end

#expect_offense(source, file = nil) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/rubocop/rspec/expect_offense.rb', line 76

def expect_offense(source, file = nil)
  RuboCop::Formatter::DisabledConfigFormatter
    .config_to_allow_offenses = {}
  RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
  cop.instance_variable_get(:@options)[:auto_correct] = true

  expected_annotations = AnnotatedSource.parse(source)

  if expected_annotations.plain_source == source
    raise 'Use `expect_no_offenses` to assert that no offenses are found'
  end

  @processed_source = parse_source(expected_annotations.plain_source,
                                   file)

  unless @processed_source.valid_syntax?
    raise 'Error parsing example code'
  end

  _investigate(cop, @processed_source)
  actual_annotations =
    expected_annotations.with_offense_annotations(cop.offenses)

  expect(actual_annotations.to_s).to eq(expected_annotations.to_s)
end