Skip to content

Commit bc8f733

Browse files
authored
Some improvements for the work from PR #1753 (#1809)
I had a half-finished review of PR #1753, then got a bad cold. Now you merged it already, so I moved my review comments into this PR here. Most changes are just minor. One notable change is, that I changed the order of cglib and mockito. Even though cglib is deprecated, I think it should sill be selected before mockito for consistency. If you currently have cglib on the classpath but no byte buddy, it will be used, then with the new version suddenly mockito will be used which could change the behavior. It swapped the order, so that mockito is below, so that it is just used as fallback for cases that couldn't be handled before. What do you think? Another notable change is, that I changed the toString of the mock maker settings implementations that just returned the mock maker id before and now return "mockito mock maker settings" or "<ID> simple mock maker settings". What do you think?
1 parent 582f78b commit bc8f733

17 files changed

Lines changed: 68 additions & 65 deletions

File tree

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ def configureJavadoc(TaskProvider javadoc, boolean root = false) {
436436
options.addStringOption('source', '1.8')
437437
links "https://docs.groovy-lang.org/docs/groovy-$groovyVersion/html/gapi/"
438438
links "https://junit.org/junit4/javadoc/latest/"
439+
links "https://javadoc.io/doc/org.mockito/mockito-core/${libs.versions.mockito5.get()}/"
439440
// Use offline package list, as the JavaVersion is fixed to 8 for the time being
440441
// and Hamcrest has certificate issues for their domain.
441442
linksOffline("https://docs.oracle.com/javase/8/docs/api/", "${root ? '' : '../'}javadoc/java-8")

docs/extensions.adoc

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,24 +1060,24 @@ The following mock makers are built-in, and are selected in this order:
10601060
* `java-proxy`: Uses the `java.lang.reflect.Proxy` API to create mocks of interfaces.
10611061
* `byte-buddy`: Uses https://bytebuddy.net/[Byte Buddy] to create mock objects.
10621062
** Requires `net.bytebuddy:byte-buddy` 1.9+ on the class path.
1063-
* `mockito`: Uses https://site.mockito.org/[Mockito] to create mocks of classes.
1064-
** Can be configured to use additional Mockito feature like mock `Serializable`
1065-
** Requires `org.mockito:mockito-core` 4.11+ on the class path.
10661063
* `cglib`: Deprecated: Uses https://github.com/cglib/cglib[CGLIB] to create mock objects.
10671064
** Requires `cglib:cglib-nodep` 3.2.0+ on the class path.
1065+
* `mockito`: Uses https://site.mockito.org/[Mockito] to create mock objects.
1066+
** Can be configured to use additional Mockito feature like mock `Serializable`
1067+
** Requires `org.mockito:mockito-core` 4.11+ on the class path.
10681068

10691069
.Capabilities of the different built-in mock makers
1070-
[cols=".^~h,^.^15,^.^15,^.^15,^.^18"]
1070+
[cols=".^~h,^.^15,^.^15,^.^15,^.^15"]
10711071
|===
10721072
^| Capability | `java-proxy` | `byte-buddy` | `cglib` | `mockito`
10731073

1074-
| Interface | ✔ | ✔ | ✔ |
1075-
| Class | ✘ | ✔ | ✔ |
1076-
| Additional Interfaces | ✔ | ✔ | ✔ |
1077-
| Explicit Constructor Arguments | ✘ | ✔ | ✔ |
1078-
| Final Class | ✘ | ✘ | ✘ |
1079-
| Final Method | ✘ | ✘ | ✘ |
1080-
| Static Method | ✘ | ✘ | ✘ |
1074+
| Interface | ✔ | ✔ | ✔ |
1075+
| Class | ✘ | ✔ | ✔ |
1076+
| Additional Interfaces | ✔ | ✔ | ✔ |
1077+
| Explicit Constructor Arguments | ✘ | ✔ | ✔ |
1078+
| Final Class | ✘ | ✘ | ✘ |
1079+
| Final Method | ✘ | ✘ | ✘ |
1080+
| Static Method | ✘ | ✘ | ✘ |
10811081
|===
10821082

10831083
The class `spock.mock.MockMakers` provides constants and methods for the built-in mock makers.
@@ -1097,27 +1097,27 @@ include::{sourcedir}/extension/MockMakerConfigurationDocSpec.groovy[tag=mock-mak
10971097
The `mockito` Mock Maker provides the ability to mock final types, enums and final methods.
10981098
The mocking of final classes is automatically enabled, if `org.mockito:mockito-core` 4.11+ is on the class path.
10991099

1100-
For mocking of final methods, you need to select `mockito` during mock construction,
1100+
For mocking of final methods, you need to select the `mockito` mock maker during mock construction,
11011101
like:
11021102
[source,groovy,indent=0]
11031103
----
11041104
include::{sourcedir}/interaction/MockMakerDocSpec.groovy[tag=mockito]
11051105
----
11061106

1107-
If you want to make final method mockable by default, you can select this mock maker as the preferred mock maker.
1108-
It can't mock `native` methods, see Mockito for details.
1107+
If you want to make final methods mockable by default, you can select this mock maker as the preferred mock maker.
1108+
It can't mock native methods, see https://javadoc.io/doc/org.mockito/mockito-core/5.6.0/org/mockito/Mockito.html#Mocking_Final[the Mockito documentation] for details.
11091109

1110-
CAUTION: If you try to mock a final method without a Mock Maker supporting it.
1111-
It will silently fail, without honoring your specified interactions.
1110+
CAUTION: If you try to mock a final method without a Mock Maker supporting it,
1111+
it will silently fail, without honoring your specified interactions.
11121112

1113-
You can configure the created mocks with the `org.mockito.MockSettings` during construction to use features provided by Mockito:
1113+
You can configure the created mock objects using the interface `org.mockito.MockSettings` during the construction to use features provided by Mockito:
11141114

11151115
[source,groovy,indent=0]
11161116
----
11171117
include::{sourcedir}/interaction/MockMakerDocSpec.groovy[tag=mock-serializable]
11181118
----
11191119

1120-
The `mockito` uses the `org.mockito.MockMakers.INLINE` under the hood,
1120+
The `mockito` mock maker uses `org.mockito.MockMakers.INLINE` under the hood,
11211121
please see the Mockito manual "Mocking final types, enums and final methods" for all pros and cons,
11221122
when using `org.mockito.MockMakers.INLINE`.
11231123

gradle/libs.versions.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ groovy3 = '3.0.19'
44
groovy4 = '4.0.15'
55
jacoco = '0.8.11'
66
asm = '9.6'
7+
mockito4 = '4.11.0'
8+
mockito5 = '5.6.0'
79

810
[libraries]
911
jetbrains-annotations = "org.jetbrains:annotations:24.0.1"
@@ -17,8 +19,8 @@ hamcrest = "org.hamcrest:hamcrest:2.2"
1719
jaxb = "javax.xml.bind:jaxb-api:2.3.1"
1820
junit4 = "junit:junit:4.13.2"
1921
log4j = "log4j:log4j:1.2.17"
20-
mockito4 = "org.mockito:mockito-core:4.11.0"
21-
mockito5 = "org.mockito:mockito-core:5.6.0"
22+
mockito4 = { module = "org.mockito:mockito-core", version.ref = "mockito4" }
23+
mockito5 = { module = "org.mockito:mockito-core", version.ref = "mockito5" }
2224
objenesis = "org.objenesis:objenesis:3.3"
2325
# This needs a classifier, but is has to be specified on the usage end https://melix.github.io/blog/2021/03/version-catalogs-faq.html#_why_can_t_i_use_excludes_or_classifiers
2426
jacoco-agent = { module = "org.jacoco:org.jacoco.agent", version.ref = "jacoco" }

spock-core/src/main/java/org/spockframework/mock/runtime/CglibMockMaker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public Set<MockMakerCapability> getCapabilities() {
4848

4949
@Override
5050
public int getPriority() {
51-
return 400;
51+
return 300;
5252
}
5353

5454
@Override

spock-core/src/main/java/org/spockframework/mock/runtime/IMockMaker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ public MockMakerId getMockMakerId() {
237237

238238
@Override
239239
public String toString() {
240-
return id.toString();
240+
return id + " simple mock maker settings";
241241
}
242242
};
243243
}

spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMaker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public Set<MockMakerCapability> getCapabilities() {
6363

6464
@Override
6565
public int getPriority() {
66-
return 300;
66+
return 400;
6767
}
6868

6969
@Override

spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMakerImpl.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@
3232
import org.spockframework.mock.MockNature;
3333
import org.spockframework.mock.runtime.IMockMaker;
3434
import org.spockframework.mock.runtime.IProxyBasedMockInterceptor;
35-
import org.spockframework.runtime.GroovyRuntimeUtil;
3635
import org.spockframework.util.ExceptionUtil;
37-
import org.spockframework.util.ObjectUtil;
3836
import org.spockframework.util.ReflectionUtil;
3937
import org.spockframework.util.ThreadSafe;
4038

@@ -71,7 +69,7 @@ Object makeMock(IMockMaker.IMockCreationSettings settings) throws CannotCreateMo
7169
mockitoSettings.extraInterfaces(settings.getAdditionalInterface().toArray(CLASS_ARRAY));
7270
}
7371
if (settings.getConstructorArgs() != null) {
74-
mockitoSettings.useConstructor(settings.getConstructorArgs().toArray(GroovyRuntimeUtil.EMPTY_ARGUMENTS));
72+
mockitoSettings.useConstructor(settings.getConstructorArgs().toArray());
7573
} else if (settings.getMockNature() == MockNature.SPY) {
7674
//We need to say Mockito it shall use the constructor otherwise it will not initialize fields of the spy, see org.mockito.Mockito.spy(java.lang.Class<T>), which does the same
7775
mockitoSettings.useConstructor();
@@ -82,7 +80,7 @@ Object makeMock(IMockMaker.IMockCreationSettings settings) throws CannotCreateMo
8280

8381
applyMockMakerSettingsFromUser(settings, mockitoSettings);
8482

85-
MockCreationSettings<Object> mockitoCreationSettings = ObjectUtil.uncheckedCast(mockitoSettings.build(settings.getMockType()));
83+
MockCreationSettings<?> mockitoCreationSettings = mockitoSettings.build(settings.getMockType());
8684
SpockMockHandler handler = new SpockMockHandler(settings.getMockInterceptor());
8785

8886
return inlineMockMaker.createMock(mockitoCreationSettings, handler);

spock-core/src/main/java/org/spockframework/mock/runtime/mockito/MockitoMockMakerSettings.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
import groovy.lang.Closure;
2020
import org.mockito.MockSettings;
2121
import org.spockframework.mock.runtime.IMockMaker;
22+
import org.spockframework.runtime.GroovyRuntimeUtil;
2223
import spock.mock.MockMakers;
2324

2425
import static java.util.Objects.requireNonNull;
2526
import static org.spockframework.mock.runtime.IMockMaker.MockMakerId;
27+
import static org.spockframework.util.ObjectUtil.uncheckedCast;
2628

2729
public final class MockitoMockMakerSettings implements IMockMaker.IMockMakerSettings {
2830
private final Closure<?> mockitoCode;
@@ -40,20 +42,16 @@ public MockMakerId getMockMakerId() {
4042
return MockMakers.mockito.getMockMakerId();
4143
}
4244

43-
void applySettings(MockSettings mockSettings) {
44-
requireNonNull(mockSettings);
45-
callClosure(mockitoCode, mockSettings);
46-
}
47-
48-
private static void callClosure(Closure<?> closure, Object delegate) {
49-
Closure<?> settingsClosure = (Closure<?>) closure.clone();
50-
settingsClosure.setResolveStrategy(Closure.DELEGATE_FIRST);
51-
settingsClosure.setDelegate(delegate);
52-
settingsClosure.call(delegate);
45+
void applySettings(MockSettings mockitoSettings) {
46+
requireNonNull(mockitoSettings);
47+
Closure<?> mockitoCode = uncheckedCast(this.mockitoCode.clone());
48+
mockitoCode.setResolveStrategy(Closure.DELEGATE_FIRST);
49+
mockitoCode.setDelegate(mockitoSettings);
50+
GroovyRuntimeUtil.invokeClosure(mockitoCode, mockitoSettings);
5351
}
5452

5553
@Override
5654
public String toString() {
57-
return getMockMakerId().toString();
55+
return getMockMakerId() + " mock maker settings";
5856
}
5957
}

spock-core/src/main/java/spock/mock/MockMakers.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import groovy.lang.Closure;
2020
import groovy.lang.DelegatesTo;
2121
import groovy.transform.stc.ClosureParams;
22-
import groovy.transform.stc.FromString;
22+
import groovy.transform.stc.SimpleType;
23+
import org.mockito.MockSettings;
2324
import org.spockframework.mock.runtime.ByteBuddyMockMaker;
2425
import org.spockframework.mock.runtime.CglibMockMaker;
2526
import org.spockframework.mock.runtime.IMockMaker;
@@ -79,8 +80,10 @@ private MockMakers() {
7980
* </ul>
8081
*/
8182
public static final IMockMaker.IMockMakerSettings javaProxy = simple(JavaProxyMockMaker.ID);
83+
8284
/**
83-
* Uses <a href="https://site.mockito.org/">mockito</a> to create mocks of final classes, enums and final methods.
85+
* Uses <a href="https://site.mockito.org/">Mockito</a> to create mocks,
86+
* which also supports final classes, enums and final methods.
8487
*
8588
* <p>The supported mocking features are:
8689
* <ul>
@@ -91,13 +94,14 @@ private MockMakers() {
9194
* <li>{@code FINAL_CLASS}</li>
9295
* <li>{@code FINAL_METHOD}</li>
9396
*
94-
* <p>It uses the mockito {@code org.mockito.MockMakers.INLINE} under the hood,
95-
* please see the mockito manual for all pros and cons, when using {@code MockMakers.INLINE}.
97+
* <p>It uses {@link org.mockito.MockMakers#INLINE} under the hood,
98+
* please see the Mockito manual for all pros and cons, when using {@code MockMakers.INLINE}.
9699
*/
97100
public static final IMockMaker.IMockMakerSettings mockito = simple(MockitoMockMaker.ID);
98101

99102
/**
100-
* Uses <a href="https://site.mockito.org/">mockito</a> to create mocks of final classes, enums and final methods.
103+
* Uses <a href="https://site.mockito.org/">Mockito</a> to create mocks,
104+
* which also supports final classes, enums and final methods.
101105
*
102106
* <p>The supported mocking features are:
103107
* <ul>
@@ -108,13 +112,13 @@ private MockMakers() {
108112
* <li>{@code FINAL_CLASS}</li>
109113
* <li>{@code FINAL_METHOD}</li>
110114
*
111-
* <p>It uses the mockito {@code org.mockito.MockMakers.INLINE} under the hood,
112-
* please see the mockito manual for all pros and cons, when using {@code MockMakers.INLINE}.
115+
* <p>It uses {@link org.mockito.MockMakers#INLINE} under the hood,
116+
* please see the Mockito manual for all pros and cons, when using {@code MockMakers.INLINE}.
113117
*
114-
* @param settingsCode the code to execute to configure {@code org.mockito.MockSettings} for further configuration of the mock to create
118+
* @param settingsCode the code to execute to configure {@link MockSettings} for further configuration of the mock to create
115119
*/
116120
public static IMockMaker.IMockMakerSettings mockito(@DelegatesTo(type = "org.mockito.MockSettings", strategy = Closure.DELEGATE_FIRST)
117-
@ClosureParams(value = FromString.class, options = "org.mockito.MockSettings")
121+
@ClosureParams(value = SimpleType.class, options = "org.mockito.MockSettings")
118122
Closure<?> settingsCode) {
119123
return MockitoMockMakerSettings.createSettings(settingsCode);
120124
}

spock-specs/mock-integration/mock-integration.gradle

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def codeGenerationLibraries = [
3030
mockito4 : configurations.mockito4
3131
]
3232

33-
if (rootProject.ext.javaVersion >= 11) {
33+
if (rootProject.javaVersion >= 11) {
3434
//Mockito 5 requires at least Java 11 to run
3535
codeGenerationLibraries.put("mockito5", configurations.mockito5)
3636
}
@@ -41,17 +41,17 @@ codeGenerationLibraries.each { key, config ->
4141
classpath += config
4242

4343
if (key == "cglib") {
44-
if (rootProject.ext.javaVersion >= 17) {
44+
if (rootProject.javaVersion >= 17) {
4545
jvmArgs(
4646
//cglib requires access to java.lang.ClassLoader.defineClass() from net.sf.cglib.core.ReflectUtils
4747
"--add-opens=java.base/java.lang=ALL-UNNAMED"
4848
)
4949
}
50-
onlyIf { rootProject.ext.javaVersion < 21}
50+
onlyIf { rootProject.javaVersion < 21}
5151
}
5252
if (key == "mockito4") {
5353
// mockito4 only supports up to Java 20
54-
onlyIf { rootProject.ext.javaVersion < 21}
54+
onlyIf { rootProject.javaVersion < 21}
5555
}
5656
}
5757
tasks.register("test${key.capitalize()}WithObjenesis", Test) {
@@ -60,17 +60,17 @@ codeGenerationLibraries.each { key, config ->
6060
classpath += configurations.objenesis
6161

6262
if (key == "cglib") {
63-
if (rootProject.ext.javaVersion >= 17) {
63+
if (rootProject.javaVersion >= 17) {
6464
jvmArgs(
6565
//cglib requires access to java.lang.ClassLoader.defineClass() from net.sf.cglib.core.ReflectUtils
6666
"--add-opens=java.base/java.lang=ALL-UNNAMED"
6767
)
6868
}
69-
onlyIf { rootProject.ext.javaVersion < 21}
69+
onlyIf { rootProject.javaVersion < 21}
7070
}
7171
if (key == "mockito4") {
7272
// mockito4 only supports up to Java 20
73-
onlyIf { rootProject.ext.javaVersion < 21}
73+
onlyIf { rootProject.javaVersion < 21}
7474
}
7575
}
7676
}

0 commit comments

Comments
 (0)