module RuboCop::RSpec::ExpectOffense

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.

@example Usage

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

@example 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.'
)

Autocorrection can be tested using ‘expect_correction` after `expect_offense`.

@example ‘expect_offense` and `expect_correction`

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

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

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

@example ‘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

If your code has variables of different lengths, you can use the following markers to format your template by passing the variables as a keyword arguments:

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:

@example ‘^{}` empty line offense

expect_offense(<<~RUBY)

  ^{} Missing frozen string literal comment.
  puts 1
RUBY