Skip to content

Commit af227e7

Browse files
authored
Guard CleanupMockitoImports against removing static imports associated with MethodInvocations having null or unknown type information. (#228)
1 parent 413829a commit af227e7

File tree

2 files changed

+136
-6
lines changed

2 files changed

+136
-6
lines changed

src/main/java/org/openrewrite/java/testing/mockito/CleanupMockitoImports.java

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,18 @@
2020
import org.openrewrite.TreeVisitor;
2121
import org.openrewrite.internal.lang.Nullable;
2222
import org.openrewrite.java.JavaIsoVisitor;
23+
import org.openrewrite.java.search.FindMissingTypes;
2324
import org.openrewrite.java.search.UsesType;
2425
import org.openrewrite.java.tree.J;
26+
import org.openrewrite.java.tree.JavaSourceFile;
2527

2628
import java.time.Duration;
29+
import java.util.Arrays;
30+
import java.util.List;
31+
import java.util.stream.Collectors;
2732

2833
/**
29-
* Orders imports and removes unused imports from classes which import symbols from the "org.mockito" package.
34+
* Removes unused "org.mockito" imports.
3035
*/
3136
public class CleanupMockitoImports extends Recipe {
3237
@Override
@@ -36,7 +41,7 @@ public String getDisplayName() {
3641

3742
@Override
3843
public String getDescription() {
39-
return "Removes unused imports `org.mockito` import symbols.";
44+
return "Removes unused `org.mockito` import symbols, unless its possible they are associated with method invocations having null or unknown type information.";
4045
}
4146

4247
@Override
@@ -56,12 +61,45 @@ protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
5661
}
5762

5863
public static class CleanupMockitoImportsVisitor extends JavaIsoVisitor<ExecutionContext> {
64+
private static final List<String> MOCKITO_METHOD_NAMES = Arrays.asList("mock", "mockingDetails", "spy",
65+
"stub", "when", "verify", "reset", "verifyNoMoreInteractions", "verifyZeroInteractions", "stubVoid",
66+
"doThrow", "doCallRealMethod", "doAnswer", "doNothing", "doReturn", "inOrder", "ignoreStubs",
67+
"times", "never", "atLeastOnce", "atLeast", "atMost", "calls", "only", "timeout", "after");
5968
@Override
60-
public J.Import visitImport(J.Import _import, ExecutionContext executionContext) {
61-
if (_import.getPackageName().startsWith("org.mockito")) {
62-
maybeRemoveImport(_import.getPackageName() + "." + _import.getClassName());
69+
public JavaSourceFile visitJavaSourceFile(JavaSourceFile cu, ExecutionContext executionContext) {
70+
JavaSourceFile sf = super.visitJavaSourceFile(cu, executionContext);
71+
72+
final List<String> unknownTypeMethodInvocationNames = FindMissingTypes.findMissingTypes(cu)
73+
.stream().map(FindMissingTypes.MissingTypeResult::getJ)
74+
.filter(J.MethodInvocation.class::isInstance)
75+
.map(J.MethodInvocation.class::cast)
76+
.map(J.MethodInvocation::getSimpleName).collect(Collectors.toList());
77+
78+
for (J.Import _import : cu.getImports()) {
79+
if (_import.getPackageName().startsWith("org.mockito")) {
80+
if (_import.isStatic()) {
81+
String staticName = _import.getQualid().getSimpleName();
82+
if ("*".equals(staticName) && !possibleMockitoMethod(unknownTypeMethodInvocationNames)) {
83+
maybeRemoveImport(_import.getPackageName() + "." + _import.getClassName());
84+
} else if (!unknownTypeMethodInvocationNames.contains(staticName)) {
85+
maybeRemoveImport(_import.getPackageName() + "." + _import.getClassName() + "." + staticName);
86+
}
87+
} else {
88+
maybeRemoveImport(_import.getPackageName() + "." + _import.getClassName());
89+
}
90+
}
91+
}
92+
return sf;
93+
}
94+
95+
private boolean possibleMockitoMethod(List<String> methodNamesHavingNullType) {
96+
for (String missingMethod : methodNamesHavingNullType) {
97+
if (MOCKITO_METHOD_NAMES.contains(missingMethod)) {
98+
return true;
99+
}
63100
}
64-
return _import;
101+
102+
return false;
65103
}
66104
}
67105
}

src/test/kotlin/org/openrewrite/java/testing/mockito/CleanupMockitoImportsTest.kt

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,98 @@ class CleanupMockitoImportsTest : JavaRecipeTest {
5454
public class A {}
5555
"""
5656
)
57+
58+
@Test
59+
fun `do not remove static import when possibly associated with method invocation having a null type`() =
60+
assertUnchanged(
61+
before = """
62+
import static org.mockito.Mockito.when;
63+
64+
class MyObjectTest {
65+
MyObject myObject;
66+
67+
void test() {
68+
when(myObject.getSomeField()).thenReturn("testValue");
69+
}
70+
}
71+
"""
72+
)
73+
74+
@Test
75+
fun `remove unused mockito static import`() = assertChanged(
76+
dependsOn = arrayOf(
77+
"""
78+
class MyObject {
79+
String getSomeField(){return null;}
80+
}
81+
"""
82+
),
83+
before = """
84+
import static org.mockito.Mockito.when;
85+
import static org.mockito.Mockito.after;
86+
import org.junit.jupiter.api.Test;
87+
import org.mockito.Mock;
88+
89+
class MyObjectTest {
90+
@Mock
91+
MyObject myObject;
92+
93+
void test() {
94+
when(myObject.getSomeField()).thenReturn("testValue");
95+
}
96+
}
97+
""",
98+
after = """
99+
import static org.mockito.Mockito.when;
100+
import org.junit.jupiter.api.Test;
101+
import org.mockito.Mock;
102+
103+
class MyObjectTest {
104+
@Mock
105+
MyObject myObject;
106+
107+
void test() {
108+
when(myObject.getSomeField()).thenReturn("testValue");
109+
}
110+
}
111+
"""
112+
)
113+
114+
@Test
115+
fun `preserve star imports`() = assertUnchanged(
116+
before = """
117+
package mockito.example;
118+
119+
import java.util.List;
120+
121+
import static org.mockito.Mockito.*;
122+
123+
public class MockitoArgumentMatchersTest {
124+
static class Foo {
125+
boolean bool(String str, int i, Object obj) { return false; }
126+
}
127+
128+
public void usesMatchers() {
129+
Foo mockFoo = mock(Foo.class);
130+
when(mockFoo.bool(anyString(), anyInt(), any(Object.class))).thenReturn(true);
131+
}
132+
}
133+
"""
134+
)
135+
136+
@Test
137+
fun `remove unused star import`() = assertChanged(
138+
before = """
139+
import static org.mockito.Mockito.*;
140+
141+
public class MockitoArgumentMatchersTest {
142+
}
143+
""",
144+
after = """
145+
public class MockitoArgumentMatchersTest {
146+
}
147+
"""
148+
)
57149
}
58150

59151

0 commit comments

Comments
 (0)