|
| 1 | += Upgrade to Version 2.x |
| 2 | +:doctype: book |
| 3 | + |
| 4 | +== Configuration File Update |
| 5 | + |
| 6 | +In version 2.x: |
| 7 | + |
| 8 | + - `RSpec/InvalidPredicateMatcher` cop is removed |
| 9 | + - `CustomIncludeMethods` configuration option for `RSpec/EmptyExampleGroup` is removed |
| 10 | + - cop departments are nested for cops with a department that doesn’t match the extension name (`Capybara`, `FactoryBot`, `Rails`) |
| 11 | + - `AllCops/RSpec/Patterns`/`AllCops/FactoryBot/Patterns` options are removed |
| 12 | + - Calling `super` from `#on_new_investigation` defined in a cop is mandatory now |
| 13 | + - In specs, do not define `cop` |
| 14 | + |
| 15 | +[discrete] |
| 16 | +=== Adjust the configuration of `RSpec/EmptyExampleGroup` |
| 17 | + |
| 18 | +[source,yaml] |
| 19 | +---- |
| 20 | +# .rubocop.yml |
| 21 | +
|
| 22 | +# Before |
| 23 | +RSpec/EmptyExampleGroup: |
| 24 | + CustomIncludeMethods: |
| 25 | + - include_tests |
| 26 | +
|
| 27 | +# After |
| 28 | +RSpec: |
| 29 | + Language: |
| 30 | + Includes: |
| 31 | + Examples: |
| 32 | + - include_tests |
| 33 | +---- |
| 34 | + |
| 35 | +=== Add a top-level `RSpec` department |
| 36 | + |
| 37 | +RuboCop extensions had cops with clashing names and departments, e.g. both `rspec-rails` and `rubocop-rspec` had `Rails::HttpStatus` cops. |
| 38 | +To avoid issues, e.g. inability to disable just one of the cops, each extension now has its own uber-department. |
| 39 | +Expectedly, RuboCop RSpec’s uber-department name is `RSpec`. |
| 40 | +Changes are only applied to cops that don’t already have the department set to `RSpec`, i.e. `Capybara`, `FactoryBot` and `Rails`. |
| 41 | + |
| 42 | +[source,yaml] |
| 43 | +---- |
| 44 | +# .rubocop.yml |
| 45 | +
|
| 46 | +# Before |
| 47 | +Capybara/CurrentPathExpectation: |
| 48 | + Enabled: false |
| 49 | +
|
| 50 | +FactoryBot/AttributeDefinedStatically: |
| 51 | + Enabled: false |
| 52 | +
|
| 53 | +# remains the same |
| 54 | +RSpec/EmptyExampleGroup: |
| 55 | + Enabled: false |
| 56 | +
|
| 57 | +# After |
| 58 | +RSpec/Capybara/CurrentPathExpectation: |
| 59 | + Enabled: false |
| 60 | +
|
| 61 | +RSpec/FactoryBot/AttributeDefinedStatically: |
| 62 | + Enabled: false |
| 63 | +
|
| 64 | +# remains the same |
| 65 | +RSpec/EmptyExampleGroup: |
| 66 | + Enabled: false |
| 67 | +---- |
| 68 | + |
| 69 | +https://github.com/rubocop-hq/rubocop/pull/8490[Learn more about this change]. |
| 70 | + |
| 71 | + |
| 72 | +=== Use the RuboCop standard `Include` option to filter inspected files |
| 73 | + |
| 74 | +`Patterns` was a RuboCop RSpec-specific option, and RuboCop has a standard replacement. |
| 75 | + |
| 76 | +[source,yaml] |
| 77 | +---- |
| 78 | +# .rubocop.yml |
| 79 | +
|
| 80 | +# Before |
| 81 | +AllCops: |
| 82 | + RSpec/FactoryBot: |
| 83 | + Patterns: |
| 84 | + - spec/factories/**/*.rb |
| 85 | + - property/factories/**/*.rb |
| 86 | +
|
| 87 | +# After |
| 88 | +RSpec/FactoryBot: |
| 89 | + Include: |
| 90 | + - spec/factories/**/*.rb |
| 91 | + - property/factories/**/*.rb |
| 92 | +---- |
| 93 | + |
| 94 | +NOTE: Please keep in mind that `Include`'s merge mode is set to override the default settings, so if you intend to add a path while keeping the default paths, you should include the default `Include` paths in your configuration. |
| 95 | + |
| 96 | +https://github.com/rubocop-hq/rubocop-rspec/pull/1063[Learn more about this change]. |
| 97 | + |
| 98 | +== Custom Cop Update Guide |
| 99 | + |
| 100 | +Due to significant API changes, custom cops may break. |
| 101 | +Here is the summary of the changes: |
| 102 | + |
| 103 | +1. The base class for cops is now `RuboCop::Cop::RSpec::Base` instead of `RuboCop::Cop::RSpec::Cop`. |
| 104 | + |
| 105 | +2. The module `RuboCop::Cop::RSpec::TopLevelDescribe` is replaced with a more generic `RuboCop::Cop::RSpec::TopLevelGroup`. |
| 106 | + |
| 107 | +3. `RuboCop::RSpec::Language` has been completely rewritten to support dynamic RSpec DSL aliases and negated matchers to fully support third-party libraries such as RSpec Rails, Pundit, Action Policy and many others. |
| 108 | + |
| 109 | +4. RuboCop RSpec updated the dependency of RuboCop to 1.0+. |
| 110 | + |
| 111 | +Below are the necessary steps to update custom cops to work with `rubocop-rspec` version 2.x. |
| 112 | + |
| 113 | + |
| 114 | +=== Change the Parent Class |
| 115 | + |
| 116 | +Change the parent class of the custom cops from `RuboCop::Cop::RSpec::Cop` to `RuboCop::Cop::RSpec::Base`. |
| 117 | + |
| 118 | +[source,ruby] |
| 119 | +---- |
| 120 | +# Before |
| 121 | +module RuboCop |
| 122 | + module Cop |
| 123 | + module RSpec |
| 124 | + class FightPowerty < Cop |
| 125 | +
|
| 126 | +# After |
| 127 | +module RuboCop |
| 128 | + module Cop |
| 129 | + module RSpec |
| 130 | + class FightPowerty < Base |
| 131 | +---- |
| 132 | + |
| 133 | +https://github.com/rubocop-hq/rubocop-rspec/pull/962[Example pull request]. |
| 134 | + |
| 135 | + |
| 136 | +=== Replace `TopLevelDescribe` |
| 137 | + |
| 138 | +`TopLevelDescribe` was incomplete, had poor performance and did not distinguish between example groups and shared example groups. |
| 139 | + |
| 140 | +`TopLevelGroup` provides a similar interface, but instead of a single `on_top_level_describe` hook there are two, `on_top_level_example_group` and `on_top_level_group`. |
| 141 | +There’s no need yet for `on_top_level_shared_group` for RuboCop core cops, but if your custom cop needs such a hook, please feel free to send a pull request. |
| 142 | + |
| 143 | +Additionally, `single_top_level_describe?` is removed with no direct replacement. |
| 144 | +You may use `top_level_groups` query method instead, e.g. `top_level_groups.one?`. |
| 145 | + |
| 146 | +Example pull requests to replace `TopLevelDescribe` with `TopLevelGroup` [https://github.com/rubocop-hq/rubocop-rspec/pull/978[1], https://github.com/rubocop-hq/rubocop-rspec/pull/932[2], https://github.com/rubocop-hq/rubocop-rspec/pull/977[3]]. |
| 147 | + |
| 148 | + |
| 149 | +=== Change the `Language` Module Usages |
| 150 | + |
| 151 | +To allow for lazy initialization, and for loading of the language configuration after the class are loaded, a https://docs.rubocop.org/rubocop-ast/node_pattern.html#to-call-functions[function call feature of RuboCop AST] is used. |
| 152 | + |
| 153 | +The `RuboCop::RSpec::Language` is completely different now. |
| 154 | + |
| 155 | +`Hooks::ALL` and alike, and their accompanying helpers work differently. |
| 156 | + |
| 157 | +[source,ruby] |
| 158 | +---- |
| 159 | +# Before |
| 160 | +def_node_matcher :shared_context, |
| 161 | + SharedGroups::CONTEXT.block_pattern |
| 162 | +
|
| 163 | +# After |
| 164 | +def_node_matcher :shared_context, |
| 165 | + block_pattern('#SharedGroups.context') |
| 166 | +---- |
| 167 | + |
| 168 | +[source,ruby] |
| 169 | +---- |
| 170 | +# Before |
| 171 | +def_node_search :examples?, |
| 172 | + (Includes::EXAMPLES + Examples::ALL).send_pattern |
| 173 | +
|
| 174 | +# After |
| 175 | +def_node_search :examples?, |
| 176 | + send_pattern('{#Includes.examples #Examples.all}') |
| 177 | +---- |
| 178 | + |
| 179 | +[source,ruby] |
| 180 | +---- |
| 181 | +# Before |
| 182 | +def_node_search :find_rspec_blocks, |
| 183 | + ExampleGroups::ALL.block_pattern |
| 184 | +
|
| 185 | +# After |
| 186 | +def_node_search :find_rspec_blocks, |
| 187 | + block_pattern('#ExampleGroups.all') |
| 188 | +---- |
| 189 | + |
| 190 | +If you were calling Language elements directly, you have to make the same adjustments: |
| 191 | + |
| 192 | +[source,ruby] |
| 193 | +---- |
| 194 | +# Before |
| 195 | +node&.sym_type? && Hooks::Scopes::ALL.include?(node.value) |
| 196 | +
|
| 197 | +# After |
| 198 | +node&.sym_type? && Language::HookScopes.all(node.value) |
| 199 | +---- |
| 200 | + |
| 201 | +You may see a common pattern in the change. |
| 202 | +There is a small exception, though: |
| 203 | + |
| 204 | +[source,ruby] |
| 205 | +---- |
| 206 | +# Before |
| 207 | +ExampleGroups::GROUPS |
| 208 | +
|
| 209 | +# After |
| 210 | +ExampleGroups.regular |
| 211 | +
|
| 212 | +# Before |
| 213 | +Examples::EXAMPLES |
| 214 | +
|
| 215 | +# After |
| 216 | +Examples.regular |
| 217 | +---- |
| 218 | + |
| 219 | +https://github.com/rubocop-hq/rubocop-rspec/pull/956[Pull request with more examples]. |
| 220 | + |
| 221 | +=== Always call `super` from `on_new_investigation` in your cops |
| 222 | + |
| 223 | +`on_new_investigation` is now used for internal purposes, and not calling `super` from your cop involves a risk of configuration not being properly loaded, and dynamic RSpec DSL matchers won't work. |
| 224 | + |
| 225 | +NOTE: You don't have to define `on_new_investigation` in your cops unless you need to. |
| 226 | + |
| 227 | +[source,ruby] |
| 228 | +---- |
| 229 | +module RuboCop |
| 230 | + module Cop |
| 231 | + module RSpec |
| 232 | + class MultipleMemoizedHelpers < Base |
| 233 | + def on_new_investigation |
| 234 | + super # Always call `super` |
| 235 | + @example_group_memoized_helpers = {} |
| 236 | + end |
| 237 | + end |
| 238 | + end |
| 239 | + end |
| 240 | +end |
| 241 | +---- |
| 242 | + |
| 243 | +https://github.com/rubocop-hq/rubocop-rspec/pull/956[Pull request with more examples]. |
| 244 | + |
| 245 | +=== Use `:config` RSpec metadata in cop specs |
| 246 | + |
| 247 | +`:config` metadata should be added to the top-level example group of your cop spec. |
| 248 | +Doing otherwise will not pass configuration to the cop, and dynamic RSpec DSL matchers might not work. |
| 249 | + |
| 250 | +[source,ruby] |
| 251 | +---- |
| 252 | +# Before |
| 253 | +RSpec.describe 'MyMightyCop' do |
| 254 | + let(:cop) { described_class.new } |
| 255 | + # ... |
| 256 | +end |
| 257 | +
|
| 258 | +# After |
| 259 | +RSpec.describe 'MyMightyCop', :config do |
| 260 | + # `cop` is defined for you by RuboCop's shared context that is included |
| 261 | + # to example groups with :config metadata |
| 262 | +
|
| 263 | + # ... |
| 264 | +end |
| 265 | +---- |
| 266 | + |
| 267 | +https://github.com/rubocop-hq/rubocop/blob/51ff1d7e29c985732fe129082c98d66c531a2611/lib/rubocop/rspec/shared_contexts.rb#L56[RuboCop takes care of defining everything for your cop specs]. |
| 268 | + |
| 269 | +=== Conform with RuboCop API Changes |
| 270 | + |
| 271 | +The parent project, RuboCop, has API changes. |
| 272 | +While they won’t result in cop breakages, it is recommended to update cops to use new API’s. |
| 273 | +Follow the https://docs.rubocop.org/rubocop/v1_upgrade_notes[RuboCop v1 update guide] to adjust custom cops’ use of RuboCop’s auto-correction API. |
0 commit comments