Skip to content

Commit 65d2191

Browse files
committed
Stop disabling MockitoResetTestExecutionListener within a native image
Instead, MockitoResetTestExecutionListener is now only enabled if the current test class uses Mockito annotations or Mockito-related annotations in spring-test. See gh-32933
1 parent 9a4be95 commit 65d2191

File tree

3 files changed

+70
-48
lines changed

3 files changed

+70
-48
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.bean.override.mockito;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
21+
import java.util.Arrays;
22+
import java.util.concurrent.atomic.AtomicBoolean;
23+
import java.util.function.Predicate;
24+
25+
import org.springframework.util.ReflectionUtils;
26+
27+
/**
28+
* Utility class that detects {@code org.mockito} annotations as well as the
29+
* annotations in this package (like {@link MockitoBeanSettings @MockitoBeanSettings}).
30+
*
31+
* @author Simon Baslé
32+
* @author Sam Brannen
33+
*/
34+
abstract class MockitoAnnotationDetector {
35+
36+
private static final String MOCKITO_BEAN_PACKAGE = MockitoBeanSettings.class.getPackageName();
37+
38+
private static final String ORG_MOCKITO_PACKAGE = "org.mockito";
39+
40+
private static final Predicate<Annotation> isMockitoAnnotation = annotation -> {
41+
String packageName = annotation.annotationType().getPackageName();
42+
return (packageName.startsWith(MOCKITO_BEAN_PACKAGE) ||
43+
packageName.startsWith(ORG_MOCKITO_PACKAGE));
44+
};
45+
46+
static boolean hasMockitoAnnotations(Class<?> testClass) {
47+
if (isAnnotated(testClass)) {
48+
return true;
49+
}
50+
// TODO Ideally we should short-circuit the search once we've found a Mockito annotation,
51+
// since there's no need to continue searching additional fields or further up the class
52+
// hierarchy; however, that is not possible with ReflectionUtils#doWithFields. Plus, the
53+
// previous invocation of isAnnotated(testClass) only finds annotations declared directly
54+
// on the test class. So, we'll likely need a completely different approach that combines
55+
// the "test class/interface is annotated?" and "field is annotated?" checks in a single
56+
// search algorithm.
57+
AtomicBoolean found = new AtomicBoolean();
58+
ReflectionUtils.doWithFields(testClass, field -> found.set(true), MockitoAnnotationDetector::isAnnotated);
59+
return found.get();
60+
}
61+
62+
private static boolean isAnnotated(AnnotatedElement annotatedElement) {
63+
return Arrays.stream(annotatedElement.getAnnotations()).anyMatch(isMockitoAnnotation);
64+
}
65+
66+
}

spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3030
import org.springframework.context.ApplicationContext;
3131
import org.springframework.context.ConfigurableApplicationContext;
32-
import org.springframework.core.NativeDetector;
3332
import org.springframework.core.Ordered;
3433
import org.springframework.lang.Nullable;
3534
import org.springframework.test.context.TestContext;
@@ -58,14 +57,16 @@ public int getOrder() {
5857

5958
@Override
6059
public void beforeTestMethod(TestContext testContext) throws Exception {
61-
if (MockitoTestExecutionListener.mockitoPresent && !NativeDetector.inNativeImage()) {
60+
Class<?> testClass = testContext.getTestClass();
61+
if (MockitoTestExecutionListener.mockitoPresent && MockitoAnnotationDetector.hasMockitoAnnotations(testClass)) {
6262
resetMocks(testContext.getApplicationContext(), MockReset.BEFORE);
6363
}
6464
}
6565

6666
@Override
6767
public void afterTestMethod(TestContext testContext) throws Exception {
68-
if (MockitoTestExecutionListener.mockitoPresent && !NativeDetector.inNativeImage()) {
68+
Class<?> testClass = testContext.getTestClass();
69+
if (MockitoTestExecutionListener.mockitoPresent && MockitoAnnotationDetector.hasMockitoAnnotations(testClass)) {
6970
resetMocks(testContext.getApplicationContext(), MockReset.AFTER);
7071
}
7172
}

spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@
1616

1717
package org.springframework.test.context.bean.override.mockito;
1818

19-
import java.lang.annotation.Annotation;
20-
import java.lang.reflect.AnnotatedElement;
21-
import java.util.Arrays;
22-
import java.util.concurrent.atomic.AtomicBoolean;
23-
import java.util.function.Predicate;
24-
2519
import org.mockito.Mockito;
2620
import org.mockito.MockitoSession;
2721
import org.mockito.quality.Strictness;
@@ -31,7 +25,6 @@
3125
import org.springframework.test.context.support.AbstractTestExecutionListener;
3226
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
3327
import org.springframework.util.ClassUtils;
34-
import org.springframework.util.ReflectionUtils;
3528

3629
/**
3730
* {@code TestExecutionListener} that enables {@link MockitoBean @MockitoBean}
@@ -127,42 +120,4 @@ private void closeMocks(TestContext testContext) throws Exception {
127120
}
128121
}
129122

130-
131-
/**
132-
* Utility class that detects {@code org.mockito} annotations as well as the
133-
* annotations in this package (like {@link MockitoBeanSettings @MockitoBeanSettings}).
134-
*/
135-
private static class MockitoAnnotationDetector {
136-
137-
private static final String MOCKITO_BEAN_PACKAGE = MockitoBeanSettings.class.getPackageName();
138-
139-
private static final String ORG_MOCKITO_PACKAGE = "org.mockito";
140-
141-
private static final Predicate<Annotation> isMockitoAnnotation = annotation -> {
142-
String packageName = annotation.annotationType().getPackageName();
143-
return (packageName.startsWith(MOCKITO_BEAN_PACKAGE) ||
144-
packageName.startsWith(ORG_MOCKITO_PACKAGE));
145-
};
146-
147-
static boolean hasMockitoAnnotations(Class<?> testClass) {
148-
if (isAnnotated(testClass)) {
149-
return true;
150-
}
151-
// TODO Ideally we should short-circuit the search once we've found a Mockito annotation,
152-
// since there's no need to continue searching additional fields or further up the class
153-
// hierarchy; however, that is not possible with ReflectionUtils#doWithFields. Plus, the
154-
// previous invocation of isAnnotated(testClass) only finds annotations declared directly
155-
// on the test class. So, we'll likely need a completely different approach that combines
156-
// the "test class/interface is annotated?" and "field is annotated?" checks in a single
157-
// search algorithm.
158-
AtomicBoolean found = new AtomicBoolean();
159-
ReflectionUtils.doWithFields(testClass, field -> found.set(true), MockitoAnnotationDetector::isAnnotated);
160-
return found.get();
161-
}
162-
163-
private static boolean isAnnotated(AnnotatedElement annotatedElement) {
164-
return Arrays.stream(annotatedElement.getAnnotations()).anyMatch(isMockitoAnnotation);
165-
}
166-
}
167-
168123
}

0 commit comments

Comments
 (0)