Skip to content

Extend the top interaction scope to cleanup methods#2366

Open
Vampire wants to merge 1 commit into
masterfrom
vampire/extend-top-scope-to-cleanup-method
Open

Extend the top interaction scope to cleanup methods#2366
Vampire wants to merge 1 commit into
masterfrom
vampire/extend-top-scope-to-cleanup-method

Conversation

@Vampire
Copy link
Copy Markdown
Member

@Vampire Vampire commented Jun 2, 2026

Fixes #616

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7477d5df-95e6-49a3-8a1c-c0536661b384

📥 Commits

Reviewing files that changed from the base of the PR and between 46eccf5 and 14b9ff9.

📒 Files selected for processing (61)
  • docs/interaction_based_testing.adoc
  • docs/release_notes.adoc
  • spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
  • spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
  • spock-core/src/main/java/org/spockframework/mock/TooFewInvocationsError.java
  • spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java
  • spock-specs/src/test/groovy/org/spockframework/smoke/mock/GlobalInteractionsInCleanup.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation-groovy4.txt
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation-groovy5.txt
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation.txt
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/using_a_variable_in_a_cell_multiple_times_compiles.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/transforms_conditions_in_private_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
✅ Files skipped from review due to trivial changes (12)
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
  • docs/interaction_based_testing.adoc
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[1].groovy
  • docs/release_notes.adoc
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[1].groovy
🚧 Files skipped from review as they are similar to previous changes (37)
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_implicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[1].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
  • spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/GDK_method_that_looks_like_built_in_method_as_explicit_condition.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/methods_without_condition_declarations_stay_unchanged_in_specs.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
  • spock-core/src/main/java/org/spockframework/mock/TooFewInvocationsError.java
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
  • spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod_with_only_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[2].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_with_exception-[0].groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/VerifyAllMethodsAstSpec/transforms_conditions_in_private_methods.groovy
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
  • spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java
  • spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ConditionMethodsAstSpec/condition_method__conditionMethod_within_condition_method__conditionMethod-[1].groovy
  • spock-specs/src/test/groovy/org/spockframework/smoke/mock/GlobalInteractionsInCleanup.groovy

📝 Walkthrough

Walkthrough

Global interactions declared outside then: now remain active through the feature method and into an optional cleanup. MockController adds verifyLastScope(); the compiler emits verifyLastScope() at feature exits; snapshots, docs, release notes, and a smoke test were updated.

Changes

Global Interactions Now Active Through Cleanup Methods

Layer / File(s) Summary
Mock Controller API extension
spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java
Added VERIFY_LAST_SCOPE constant and verifyLastScope() synchronized method; switched assertion util import.
Compiler wiring and defensive copy
spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java, spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java, spock-core/src/main/java/org/spockframework/mock/TooFewInvocationsError.java
Cached new MethodNode for verifyLastScope(); updated SpecRewriter to emit verifyLastScope() at feature-method exits; defensively copy unmatchedInvocations in TooFewInvocationsError constructor.
Smoke test demonstrating cleanup interaction verification
spock-specs/src/test/groovy/org/spockframework/smoke/mock/GlobalInteractionsInCleanup.groovy
New spec showing stubbing in setup() and verification in cleanup() for passing and failing tests.
User documentation and release notes
docs/interaction_based_testing.adoc, docs/release_notes.adoc
Clarified interactions scope extends into optional cleanup; added breaking-change note describing cardinality and response-selection semantics.
AST transformation snapshot updates
spock-specs/src/test/resources/snapshots/**
Updated many generated snapshots to call getMockController().verifyLastScope() instead of leaveScope() at feature-method exit points.

Sequence Diagram

sequenceDiagram
  participant SpecRewriter
  participant FeatureMethod
  participant MockController
  SpecRewriter->>FeatureMethod: emit verifyLastScope() at exit
  FeatureMethod->>MockController: verifyLastScope()
  MockController->>MockController: rethrow prior interaction error / assert single scope / verify interactions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • AndreasTu
  • leonard84

Poem

🐰
I hopped through setup, left a trace,
Mocks linger softly in cleanup’s place.
Stubs stand ready, no fallback sigh,
Verify the last scope — a twinkle in my eye.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Extend the top interaction scope to cleanup methods' accurately summarizes the main change: extending mock interaction scope to be available during cleanup methods, which directly addresses the linked issue #616.
Description check ✅ Passed The PR description 'Fixes #616' is related to the changeset as it references the linked issue that documents the objective of making stubbed interactions available in cleanup methods.
Linked Issues check ✅ Passed The code changes implement the objective from issue #616: interactions declared outside then blocks now remain active through the cleanup method [616]. This is evidenced by the new verifyLastScope() method, updated scope handling in SpecRewriter, and the test case GlobalInteractionsInCleanup demonstrating successful stub usage in cleanup().
Out of Scope Changes check ✅ Passed All code changes are directly related to extending interaction scope to cleanup methods. Changes include mock controller enhancement (verifyLastScope method), compiler rewriting logic, error handling, and comprehensive test snapshots reflecting the new behavior, all within scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Member Author

Vampire commented Jun 2, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR fixes issue #616 by keeping the global (base) interaction scope alive through the cleanup() fixture method instead of removing it at the end of the feature method. The mechanism replaces the leaveScope() call injected at the end of generated feature method code with a new verifyLastScope() call that verifies lower-cardinality constraints without popping the scope off the deque, so stubs remain accessible when the Spock runner invokes cleanup() after the feature method returns.

  • MockController.verifyLastScope() is added as a peer to leaveScope(); it asserts the deque has exactly one remaining scope, verifies interactions, but does not remove the scope — enabling the cleanup() fixture method to resolve stub responses from previously declared global interactions.
  • TooFewInvocationsError now takes a defensive copy of unmatchedInvocations to guard against post-verification mutations (e.g. cleanup method invocations adding to the list after the error is constructed while the synchronized lock is held).
  • AstNodeCache is extended to cache the new verifyLastScope MethodNode, and all affected AST snapshots are updated accordingly.

Confidence Score: 4/5

The change is safe to merge; the core logic is correct and the scope-lifetime fix is well-reasoned.

The mechanism — keeping the base InteractionScope alive via verifyLastScope() instead of removing it with leaveScope() — is correctly implemented. The defensive copy in TooFewInvocationsError protects against post-verification list mutations, and the Assert guard in verifyLastScope() is unreachable in all normal execution paths. The only gaps are a minor style nit, a documentation ambiguity around cleanup: blocks, and missing test coverage for the upper-cardinality breaking change called out in the release notes.

MockController.java and GlobalInteractionsInCleanup.groovy deserve a second look — the former for the minor style nit, the latter for the missing upper-cardinality test case.

Important Files Changed

Filename Overview
spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java Adds verifyLastScope() that verifies the base scope without removing it; a double-space nit is present in the Assert.that call.
spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java Replaces MockController_LeaveScope with MockController_VerifyLastScope for FeatureMethod in visitMethodAgain; position relative to cleanup is correct.
spock-core/src/main/java/org/spockframework/mock/TooFewInvocationsError.java Adds a defensive copy of unmatchedInvocations to prevent post-verification mutations from corrupting the error's diagnostic data.
spock-specs/src/test/groovy/org/spockframework/smoke/mock/GlobalInteractionsInCleanup.groovy New spec exercising the feature; covers the happy path and test-failure path, but lacks coverage for the upper-cardinality-exceeded-in-cleanup breaking change.
docs/interaction_based_testing.adoc Single-sentence update extending scope lifetime to the cleanup method; could be more precise about cleanup: blocks vs the cleanup() fixture method.

Sequence Diagram

sequenceDiagram
    participant Runner as Spock Runner
    participant Feature as Feature Method
    participant MC as MockController
    participant Scope as InteractionScope (base)
    participant Cleanup as cleanup() Fixture Method

    Runner->>MC: "new MockController() — addFirst(InteractionScope)"
    Runner->>Feature: "setup() adds interactions via addInteraction()"
    activate Scope
    Runner->>Feature: "execute feature body (given/when/then/expect)"
    Note over Feature,Scope: Stubs active during feature body
    Feature->>MC: "verifyLastScope() [NEW — replaces leaveScope()]"
    MC->>Scope: "verifyInteractions() — lower cardinality check"
    Note over MC,Scope: Scope NOT removed — stays in deque
    Feature-->>Runner: "return (scope still live)"
    Runner->>Cleanup: "cleanup() fixture method executes"
    Cleanup->>MC: "handle(invocation)"
    MC->>Scope: "match(invocation) — stub response returned"
    Note over Scope: Upper cardinality still enforced (breaking change)
    Cleanup-->>Runner: done
    deactivate Scope
Loading

Reviews (1): Last reviewed commit: "Extend the top interaction scope to clea..." | Re-trigger Greptile

Comment thread spock-core/src/main/java/org/spockframework/mock/runtime/MockController.java Outdated
Comment thread docs/interaction_based_testing.adoc
@Vampire Vampire force-pushed the vampire/extend-top-scope-to-cleanup-method branch from 2a205d2 to 46eccf5 Compare June 2, 2026 16:49
@Vampire Vampire force-pushed the vampire/extend-top-scope-to-cleanup-method branch from 46eccf5 to 14b9ff9 Compare June 3, 2026 13:08
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 3, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 82.18%. Comparing base (dd81cb1) to head (14b9ff9).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
...rg/spockframework/mock/runtime/MockController.java 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #2366   +/-   ##
=========================================
  Coverage     82.17%   82.18%           
- Complexity     4831     4832    +1     
=========================================
  Files           473      473           
  Lines         15051    15057    +6     
  Branches       1912     1912           
=========================================
+ Hits          12368    12374    +6     
  Misses         1991     1991           
  Partials        692      692           
Files with missing lines Coverage Δ
...java/org/spockframework/compiler/AstNodeCache.java 100.00% <100.00%> (ø)
...java/org/spockframework/compiler/SpecRewriter.java 94.17% <100.00%> (ø)
...rg/spockframework/mock/TooFewInvocationsError.java 96.00% <100.00%> (ø)
...rg/spockframework/mock/runtime/MockController.java 96.38% <75.00%> (-1.09%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented Jun 3, 2026

✅ All tests passed ✅

🏷️ Commit: 14b9ff9
▶️ Tests: 0 executed
⚪️ Checks: 61/61 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot use stubbed methods in cleanup method if the test succeeds

1 participant