Class: RuboCop::Cop::RSpecParity::SufficientContexts
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::RSpecParity::SufficientContexts
- Includes:
- DepartmentConfig, SpecFileFinder
- Defined in:
- lib/rubocop/cop/rspec_parity/sufficient_contexts.rb
Overview
Ensures that specs have at least as many contexts as the method has branches.
This cop helps ensure thorough test coverage by checking that complex methods with multiple branches (if/elsif/else, case/when, &&, ||, ternary) have corresponding context blocks in their specs to test each branch.
Defined Under Namespace
Classes: Branch, BranchTally, CoverageGap, ParsedSpec, ScanState, SpecCoverage
Constant Summary collapse
- COVERAGE_MSG =
Used when CoversAnnotations is on: names one still-uncovered branch and gives the exact context to add. Re-running advances to the next gap as branches get annotated, so the message stays short instead of listing all.
"Missing coverage for `%<token>s` (%<location>s) — " \ "%<missing>d of %<branches>d %<branch_word>s untested. " \ "Add `context '...' do # rspec_parity:covers %<token>s` to mark it covered."
- MANY_MSG =
Used when many branches are missing: too many to walk one at a time, so point at the CLI that lists them all instead.
"%<missing>d of %<branches>d %<branch_word>s untested. " \ "Run `bundle exec rspec-parity-cover %<location>s` for the full list."
- MANY_UNCOVERED_BRANCHES =
Above this many missing branches, switch from the per-branch message to the CLI pointer.
3- COUNT_MSG =
Used when CoversAnnotations is off (no annotation guidance to give).
"Method `%<method_name>s` is missing coverage for %<missing>d of %<branches>d %<branch_word>s."- TRACED_SUFFIX =
" (including branches from: %<traced>s)"- ORPHAN_SUFFIX =
" `rspec_parity:covers` annotation `%<label>s` matches no branch%<hint>s"- ANNOTATION_PATTERN =
/#\s*rspec_parity:covers\s+(.+?)\s*\z/- APP_DIR_PATTERN =
%r{/app/}- EXCLUDED_METHODS =
%w[initialize].freeze
- EXCLUDED_PATTERNS =
[ /^before_/, /^after_/, /^around_/, /^validate_/, /^autosave_/ ].freeze
- GUARD_TERMINATOR_TYPES =
Node types whose presence as a one-armed ‘if` body makes it a guard clause.
%i[return break next redo retry].freeze
Constants included from DepartmentConfig
DepartmentConfig::SHARED_CONFIG_DEFAULTS
Instance Method Summary collapse
-
#branch_inventory_for(method_node) ⇒ Object
(also: #all_branches)
Every counted branch for a method node, including those traced from single-use private helpers.
- #build_gap(inventory, coverage) ⇒ Object
- #coverage_gap(method_node, spec_content) ⇒ Object
-
#initialize(config = nil, options = nil) ⇒ SufficientContexts
constructor
A new instance of SufficientContexts.
- #on_def(node) ⇒ Object
- #on_defs(node) ⇒ Object
Constructor Details
#initialize(config = nil, options = nil) ⇒ SufficientContexts
Returns a new instance of SufficientContexts.
265 266 267 268 269 270 271 |
# File 'lib/rubocop/cop/rspec_parity/sufficient_contexts.rb', line 265 def initialize(config = nil, = nil) super @ignore_memoization = cop_config.fetch("IgnoreMemoization", true) @trace_single_use_private = cop_config.fetch("TraceSingleUsePrivateMethods", true) @covers_annotations = cop_config.fetch("CoversAnnotations", true) @call_graphs = {}.compare_by_identity end |
Instance Method Details
#branch_inventory_for(method_node) ⇒ Object Also known as: all_branches
Every counted branch for a method node, including those traced from single-use private helpers. Empty when the method has fewer than two branches or is excluded. Also aliased as all_branches for callers that want the full set regardless of spec coverage.
287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/rubocop/cop/rspec_parity/sufficient_contexts.rb', line 287 def branch_inventory_for(method_node) return [] if excluded_method?(method_name(method_node)) tally = branch_tally(method_node) if @trace_single_use_private extra = inlined_branches(method_node) tally += extra.branch_tally if extra.branch_tally end return [] if branches_from(tally) < 2 branch_inventory(tally, method_node) end |
#build_gap(inventory, coverage) ⇒ Object
316 317 318 319 320 321 322 323 |
# File 'lib/rubocop/cop/rspec_parity/sufficient_contexts.rb', line 316 def build_gap(inventory, coverage) matched, = match_annotations(inventory, coverage.annotations) covered = coverage.scenarios + matched.size return nil if covered.zero? || covered >= inventory.size CoverageGap.new(uncovered: inventory.reject { |branch| matched.include?(branch) }, annotated: matched, unannotated_specs: coverage.scenarios) end |
#coverage_gap(method_node, spec_content) ⇒ Object
309 310 311 312 313 314 |
# File 'lib/rubocop/cop/rspec_parity/sufficient_contexts.rb', line 309 def coverage_gap(method_node, spec_content) inventory = branch_inventory_for(method_node) return nil if inventory.empty? build_gap(inventory, count_contexts_for_method(spec_content.to_s, method_name(method_node))) end |
#on_def(node) ⇒ Object
273 274 275 |
# File 'lib/rubocop/cop/rspec_parity/sufficient_contexts.rb', line 273 def on_def(node) check_method(node) end |
#on_defs(node) ⇒ Object
277 278 279 |
# File 'lib/rubocop/cop/rspec_parity/sufficient_contexts.rb', line 277 def on_defs(node) check_method(node) end |