Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/interaction_based_testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ This makes sure that `subscriber` receives `"message1"` during execution of the
and `"message2"` during execution of the second `when:` block.

Interactions declared outside a `then:` block are active from their declaration until the end of the
containing feature method.
containing feature method plus an eventually existing `cleanup` method.
Comment thread
Vampire marked this conversation as resolved.

Interactions are always scoped to a particular feature method. Hence they cannot be declared in a static method,
`setupSpec` method, or `cleanupSpec` method. Likewise, mock objects should not be stored in static or `@Shared`
Expand Down
6 changes: 6 additions & 0 deletions docs/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ include::include.adoc[]
=== Breaking Changes

* Mock/Stub checks on `Comparable<T>` with `T` being something other than `Object` now compare using the java identity hash code instead of always being equal spockIssue:2352[]
* Interactions outside a `then:` block are now still active in an eventually existing `cleanup` method.
If the interaction contains a lower cardinality, this is still checked at the end of the feature method.
Upper cardinality is still relevant in the `cleanup` method, so could cause test failures where previously simply the default response of the mock object was used without cardinality check and could now cause previously working tests to fail.
This enables `cleanup` methods to get stubbed responses from mock objects which previously was only possible by using a custom default response.
If an interaction with stubbed response is present, the default response is also no longer used in the `cleanup` method because the interaction's stubbed response is used now, which could also cause existing tests to fail.
spockIssue:616[]

== 2.4 (2025-12-11)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ public class AstNodeCache {
public final MethodNode MockController_LeaveScope =
MockController.getDeclaredMethods(org.spockframework.mock.runtime.MockController.LEAVE_SCOPE).get(0);

public final MethodNode MockController_VerifyLastScope =
MockController.getDeclaredMethods(org.spockframework.mock.runtime.MockController.VERIFY_LAST_SCOPE).get(0);

public final MethodNode SpecificationContext_GetMockController =
SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_MOCK_CONTROLLER).get(0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ public void visitMethodAgain(Method method) {

// for global required interactions
if (method instanceof FeatureMethod) {
method.getStatements().add(createMockControllerCall(nodeCache.MockController_LeaveScope));
method.getStatements().add(createMockControllerCall(nodeCache.MockController_VerifyLastScope));
}

if (method instanceof VerifyAllMethod) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public TooFewInvocationsError(List<IMockInteraction> interactions, List<IMockInv
Assert.notNull(interactions);
Assert.that(interactions.size() > 0);
this.interactions = interactions;
this.unmatchedInvocations = unmatchedInvocations;
this.unmatchedInvocations = new ArrayList<>(unmatchedInvocations);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package org.spockframework.mock.runtime;

import org.spockframework.mock.*;
import org.spockframework.util.Checks;
import org.spockframework.util.Assert;
import org.spockframework.util.ExceptionUtil;

import java.util.*;
Expand Down Expand Up @@ -110,6 +110,14 @@ public synchronized void leaveScope() {
scope.verifyInteractions();
}

public static final String VERIFY_LAST_SCOPE = "verifyLastScope";

public synchronized void verifyLastScope() {
throwAnyPreviousError();
Assert.that(scopes.size() == 1, () -> "There should be exactly one scope left, but there were " + scopes.size());
scopes.getFirst().verifyInteractions();
}

private void throwAnyPreviousError() {
if (!errors.isEmpty()) throw errors.get(0);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2026 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.spockframework.smoke.mock

import org.spockframework.runtime.ConditionNotSatisfiedError
import spock.lang.FailsWith
import spock.lang.Issue
import spock.lang.Specification

import java.util.function.Function

@Issue("https://github.com/spockframework/spock/issues/616")
class GlobalInteractionsInCleanup extends Specification {
def mock = Mock(Function)
def mock2 = Mock(Function) {
apply("a") >> { "c" }
}
def mock3 = Mock(Function)

def setup() {
mock.apply("a") >> { "b" }
}

def cleanup() {
verifyAll {
mock.apply("a") == "b"
mock2.apply("a") == "c"
mock3.apply("a") == "d"
}
}

def "run a successful test"() {
given:
mock3.apply("a") >> { "d" }

expect:
true
}

@FailsWith(ConditionNotSatisfiedError)
def "run a failing test"() {
given:
mock3.apply("a") >> { "d" }

expect:
false
}
}
Comment thread
Vampire marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
"()",
0
]
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.verifyLastScope ()V
ACONST_NULL
POP
L18
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
"()",
0
]
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.verifyLastScope ()V
L18
LINENUMBER 9 L18
RETURN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
LDC Lorg/spockframework/mock/runtime/MockController;.class
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
CHECKCAST org/spockframework/mock/runtime/MockController
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.verifyLastScope ()V
ACONST_NULL
POP
L18
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class apackage.ASpec extends spock.lang.Specification {
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
java.lang.Object nothing = null
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
java.lang.Object nothing = null
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public void $spock_feature_0_0() {
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
java.lang.Object nothing = null
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void $spock_feature_0_0() {
org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0)
java.lang.Object nothing = null
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 3)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void $spock_feature_0_0() {
}
}
}
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void $spock_feature_0_0() {
}
}
}
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void $spock_feature_0_0() {
}
}
}
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void $spock_feature_0_0() {
}
}
}
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void $spock_feature_0_0() {
}
}
}
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

@org.spockframework.runtime.model.DataProviderMetadata(line = 3, dataVariables = ['a', 'b'])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

@org.spockframework.runtime.model.DataProviderMetadata(line = 3, dataVariables = ['a', 'b'])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

public java.lang.Object $spock_feature_0_0prov0() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

public java.lang.Object $spock_feature_0_0prov0() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

public java.lang.Object $spock_feature_0_0prov0() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

public java.lang.Object $spock_feature_0_0prov0() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}

public java.lang.Object $spock_feature_0_0prov0() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
/*--------- end::snapshot[] ---------*/
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public void $spock_feature_0_0() {
finally {
}
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public void $spock_feature_0_0() {
}
})
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ public void $spock_feature_0_0() {
$spock_errorCollector1.validateCollectedErrors()}
})
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public void $spock_feature_0_0() {
}
})
org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0)
this.getSpecificationContext().getMockController().leaveScope()
this.getSpecificationContext().getMockController().verifyLastScope()
}
Loading
Loading