Skip to content
Merged
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
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ if (variant == 2.5) {
// require Java 17 which isn't supported by Groovy 2.5
include "spock-spring:boot3-test"
include "spock-spring:spring6-test"
include "spock-spring:spring7-test"
}

// https://issues.apache.org/jira/projects/TAP5/issues/TAP5-2588
Expand Down
11 changes: 9 additions & 2 deletions spock-spring/spring.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ def springVersion = "4.3.5.RELEASE"
def testSpringVersions = [
(springVersion): (8..11),
"5.0.2.RELEASE": (8..11),
"6.0.0" : (17..17)
"6.0.0" : (17..21),
"7.0.6" : (17..javaVersions.max())
].findAll { javaVersion in it.value }.keySet()

dependencies {
Expand All @@ -29,6 +30,7 @@ testing {
useJUnitJupiter()
dependencies {
implementation "javax.inject:javax.inject:1"
implementation "jakarta.inject:jakarta.inject-api:2.0.1"
implementation groovylibs.groovySql // for groovy.sql.Sql
implementation libs.junit.platform.testkit
runtimeOnly libs.h2database
Expand Down Expand Up @@ -95,10 +97,15 @@ testing {

["compileTestJava", "compileTestGroovy"].each { taskName ->
tasks.named(taskName).configure {
onlyIf { false } //skip normal test task
enabled = false //skip normal test task
}
}

tasks.withType(GroovyCompile).configureEach {
// this is needed for Spring 7 bean names from parameter names
groovyOptions.parameters = true
}

tasks.named("jar", Jar) {
manifest {
attributes(
Expand Down
2 changes: 1 addition & 1 deletion spock-spring/spring3-test/spring3-test.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ dependencies {

testImplementation projects.spockCore
testImplementation "org.springframework:spring-context"
testImplementation ("org.springframework:spring-test")
testImplementation "org.springframework:spring-test"

testRuntimeOnly projects.spockSpring
}
Expand Down
2 changes: 1 addition & 1 deletion spock-spring/spring5-test/spring5-test.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dependencies {
testImplementation projects.spockSpring
testImplementation libs.junit4
testImplementation "org.springframework:spring-context"
testImplementation ("org.springframework:spring-test")
testImplementation "org.springframework:spring-test"

}

Expand Down
2 changes: 1 addition & 1 deletion spock-spring/spring6-test/spring6-test.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies {
testImplementation projects.spockSpring
testImplementation libs.junit4
testImplementation "org.springframework:spring-context"
testImplementation("org.springframework:spring-test")
testImplementation "org.springframework:spring-test"

}

Expand Down
34 changes: 34 additions & 0 deletions spock-spring/spring7-test/spring7-test.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
def springVersion = "7.0.6"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

tasks.withType(JavaCompile).configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(17)
}
options.encoding = 'UTF-8'
}

dependencies {
implementation "org.springframework:spring-core"

testImplementation projects.spockCore
testImplementation projects.spockSpring
testImplementation libs.junit4
Copy link
Copy Markdown
Member

@AndreasTu AndreasTu Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you use Junit4 here?
Is it due to the SpringRunnerTest?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea. -:-D
I just copied the spring6 to spring7 and the boot3 to boot4, changed the packages and made the adjustments to make them run.
So everything you said in both PRs is probably valid (didn't look in detail yet) but also for the existing projects.

testImplementation "org.springframework:spring-context"
testImplementation "org.springframework:spring-test"

}


configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "org.springframework" ) {
useVersion(springVersion)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.spockframework.spring7

import groovy.transform.CompileStatic
import org.springframework.context.annotation.Bean

import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService

@CompileStatic
class NoMockConfig {

@Bean
ExecutorService executor() {
throw new RuntimeException("This should not be called")
}

@Bean
ServiceExecutor serviceExecutor(ExecutorService executorService) {
return new ServiceExecutor(executorService)
}


}

class ServiceExecutor {
private final ExecutorService executorService

ServiceExecutor(ExecutorService executorService) {
this.executorService = executorService
}

def exec() {
executorService.submit({"done"} as Callable).get()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.spockframework.spring7

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification

import java.util.concurrent.Executor

@ContextConfiguration(classes = TestConfig)
class RuntimeCompatibilitySpec extends Specification {

@Autowired
Executor injectMe

def "no runtime errors are thrown"() {
expect:
1 == 1
}

def "injection works"() {
expect:
injectMe != null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.spockframework.spring7

import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification

import java.util.concurrent.ExecutorService
import java.util.concurrent.Future

@ContextConfiguration(classes = NoMockConfig)
class SpringBeanTest extends Specification {

@Autowired
ServiceExecutor serviceExecutor

@SpringBean
ExecutorService executor = Mock()

def "replace executor"() {
when:
def result = serviceExecutor.exec()

then:
result == 'mocked'
1 * executor.submit(_) >> Stub(Future) {
get() >> 'mocked'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.spockframework.spring7;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
* Make sure we still can correctly execute tests executed by the SpringRunner that are not spock tests
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringRunnerTest {
Comment on lines +12 to +14
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for any context configuration files related to SpringRunnerTest
fd -t f "SpringRunnerTest" spock-spring/spring7-test/

echo "---"
# Also search for any -context.xml files in spring7-test
fd -t f "\-context\.xml$" spock-spring/spring7-test/

echo "---"
# Check what files exist in the test directory structure
fd -t f "\.xml$|\.java$|\.groovy$" spock-spring/spring7-test/src/test | head -20

Repository: spockframework/spock

Length of output: 621


🏁 Script executed:

#!/bin/bash
# Read the SpringRunnerTest.java file to see full class content
cat -n spock-spring/spring7-test/src/test/groovy/org/spockframework/spring7/SpringRunnerTest.java

Repository: spockframework/spock

Length of output: 770


🏁 Script executed:

#!/bin/bash
# Check how other tests configure their Spring context
rg -A 3 "@ContextConfiguration|@SpringBootTest" spock-spring/spring7-test/src/test/groovy/org/spockframework/spring7/ -t groovy -t java

Repository: spockframework/spock

Length of output: 1482


🏁 Script executed:

#!/bin/bash
# Check if there's any base test class or configuration that SpringRunnerTest might inherit from
rg "class SpringRunnerTest|extends|@Configuration" spock-spring/spring7-test/src/test/groovy/org/spockframework/spring7/SpringRunnerTest.java -A 5 -B 5

Repository: spockframework/spock

Length of output: 367


Specify context configuration explicitly.

The @ContextConfiguration annotation has no classes or locations parameter, which is inconsistent with other test classes in the same directory and relies on Spring's default context discovery. Since this test doesn't use any Spring beans, either:

  • Remove @ContextConfiguration if no context is needed, or
  • Specify @ContextConfiguration(classes = {}) to explicitly define an empty context

This prevents reliance on Spring's implicit behavior which may vary across versions or configurations.

🤖 Prompt for AI Agents
In
spock-spring/spring7-test/src/test/groovy/org/spockframework/spring7/SpringRunnerTest.java
around lines 12 to 14, the @ContextConfiguration annotation is empty which
relies on Spring's default context discovery; since this test does not use any
Spring beans, either remove the @ContextConfiguration annotation entirely, or
make the configuration explicit by changing it to @ContextConfiguration(classes
= {}) so the test does not depend on implicit/version-dependent context
discovery.


@Test
public void testThatSpringRunnerExecutesCorrectly() {
Assert.assertTrue(true);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.spockframework.spring7

import groovy.transform.CompileStatic
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import spock.mock.DetachedMockFactory

import java.util.concurrent.Executor

@CompileStatic
@Configuration
class TestConfig {
@Bean
Executor executor() {
new DetachedMockFactory().Stub(Executor)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ private void checkNoSharedFieldsInjected(SpecInfo spec) {
&& (field.getReflection().isAnnotationPresent(Autowired.class)
// avoid compile-time dependency on optional classes
|| ReflectionUtil.isAnnotationPresent(field.getReflection(), "javax.annotation.Resource")
|| ReflectionUtil.isAnnotationPresent(field.getReflection(), "javax.inject.Inject"))) {
|| ReflectionUtil.isAnnotationPresent(field.getReflection(), "jakarta.annotation.Resource")
|| ReflectionUtil.isAnnotationPresent(field.getReflection(), "javax.inject.Inject")
|| ReflectionUtil.isAnnotationPresent(field.getReflection(), "jakarta.inject.Inject"))) {
throw new SpringExtensionException(
"@Shared field injection is not enabled by default therefore '%s' field cannot be injected. Refer to " +
"javadoc of %s for information on how to opt-in for @Shared field injection.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ package org.spockframework.spring

import spock.lang.Specification

import javax.inject.Inject
import javax.inject.Inject as JavaxInject
import jakarta.inject.Inject

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.*
Expand All @@ -28,6 +29,7 @@ class ContextHierarchyExample extends Specification {
@Autowired
Service1 service1

@JavaxInject
@Inject
Service2 service2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class InjectionExamples extends Specification {
}

@Requires({ReflectionUtil.isClassAvailable("javax.annotation.Resource")})
def "injecting a field by name"() {
def "injecting a field by name with javax"() {
def runner = new EmbeddedSpecRunner()

when:
Expand All @@ -50,6 +50,35 @@ import org.spockframework.spring.IService1
import org.springframework.test.context.ContextConfiguration
import spock.lang.*

@ContextConfiguration(locations = "InjectionExamples-context.xml")
class Foo extends Specification {
@Resource
IService1 myService1

def foo() {
expect:
myService1 instanceof IService1
}
}
"""

then:
noExceptionThrown()
}

@Requires({ReflectionUtil.isClassAvailable("jakarta.annotation.Resource")})
def "injecting a field by name with jakarta"() {
def runner = new EmbeddedSpecRunner()

when:
runner.run """
package org.spockframework.spring

import jakarta.annotation.Resource
import org.spockframework.spring.IService1
import org.springframework.test.context.ContextConfiguration
import spock.lang.*

@ContextConfiguration(locations = "InjectionExamples-context.xml")
class Foo extends Specification {
@Resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import org.springframework.test.context.ContextConfiguration
import spock.lang.IgnoreIf
import spock.lang.Specification

import javax.inject.Named
import javax.inject.Named as JavaxNamed
import jakarta.inject.Named

@IgnoreIf(
value = { jvm.java17Compatible },
Expand All @@ -32,23 +33,28 @@ import javax.inject.Named
class MockInjectionExample extends Specification {

@Autowired
@JavaxNamed('serviceMock')
@Named('serviceMock')
IService1 serviceMock


@Autowired
@JavaxNamed('serviceStub')
@Named('serviceStub')
IService1 serviceStub

@Autowired
@JavaxNamed('serviceSpy')
@Named('serviceSpy')
IService2 serviceSpy

@Autowired
@JavaxNamed('service2')
@Named('service2')
IService2 service2

@Autowired
@JavaxNamed('nonMock')
@Named('nonMock')
ArrayList concreteSpy;

Expand Down
Loading
Loading