Skip to content

Commit 15905d7

Browse files
Implement MockitoJUnitRunner to Extension Recipe (#709)
* Implement MockitoJUnitRunner to Extension Recipe * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 4f669aa commit 15905d7

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
import java.util.Collections;
3131
import java.util.Comparator;
3232

33+
/**
34+
* @deprecated Use MockitoJUnitRunnerToExtension instead.
35+
*/
36+
@Deprecated
3337
public class MockitoJUnitRunnerSilentToExtension extends Recipe {
3438

3539
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (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+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
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+
package org.openrewrite.java.testing.mockito;
17+
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Preconditions;
20+
import org.openrewrite.Recipe;
21+
import org.openrewrite.TreeVisitor;
22+
import org.openrewrite.java.JavaIsoVisitor;
23+
import org.openrewrite.java.JavaParser;
24+
import org.openrewrite.java.JavaTemplate;
25+
import org.openrewrite.java.search.UsesType;
26+
import org.openrewrite.java.testing.junit5.RunnerToExtension;
27+
import org.openrewrite.java.tree.J;
28+
import org.openrewrite.java.tree.TypeUtils;
29+
30+
import java.util.Arrays;
31+
import java.util.Comparator;
32+
import java.util.Optional;
33+
import java.util.concurrent.atomic.AtomicReference;
34+
35+
import static org.openrewrite.java.trait.Traits.annotated;
36+
37+
public class MockitoJUnitRunnerToExtension extends Recipe {
38+
@Override
39+
public String getDisplayName() {
40+
return "Replace JUnit 4 MockitoJUnitRunner with junit-jupiter MockitoExtension";
41+
}
42+
43+
44+
@Override
45+
public String getDescription() {
46+
return "Replace JUnit 4 MockitoJUnitRunner annotations with JUnit 5 `@ExtendWith(MockitoExtension.class)` " +
47+
"using the appropriate strictness levels (LENIENT, WARN, STRICT_STUBS).";
48+
}
49+
50+
@Override
51+
public TreeVisitor<?, ExecutionContext> getVisitor() {
52+
return Preconditions.check(new UsesType<>("org.mockito.junit.MockitoJUnitRunner*", false), new JavaIsoVisitor<ExecutionContext>() {
53+
54+
final String runWith = "@org.junit.runner.RunWith";
55+
56+
@Override
57+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
58+
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
59+
AtomicReference<Strictness> strictness = new AtomicReference<>();
60+
annotated(runWith).<AtomicReference<Strictness>>asVisitor((a, s) -> a.getTree().acceptJava(new JavaIsoVisitor<AtomicReference<Strictness>>() {
61+
@Override
62+
public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, AtomicReference<Strictness> strictness) {
63+
for (Strictness strict : Strictness.values()) {
64+
if (TypeUtils.isAssignableTo(strict.runner, fieldAccess.getTarget().getType())) {
65+
strictness.set(strict);
66+
break;
67+
}
68+
}
69+
return fieldAccess;
70+
}
71+
}, s)).visit(cd, strictness);
72+
73+
if (strictness.get() == null) { // class doesn't have MockitoJunitRunner
74+
return cd;
75+
}
76+
77+
registerAfterVisit();
78+
return getTemplate(strictness.get(), ctx)
79+
.map(t -> maybeAutoFormat(cd,
80+
t.apply(updateCursor(cd), cd.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))),
81+
ctx)).orElse(cd);
82+
}
83+
84+
private void registerAfterVisit() {
85+
doAfterVisit(new RunnerToExtension(Arrays.asList("org.mockito.junit.MockitoJUnitRunner.Silent", "org.mockito.junit.MockitoJUnitRunner.Strict", "org.mockito.junit.MockitoJUnitRunner"),
86+
"org.mockito.junit.jupiter.MockitoExtension").getVisitor());
87+
for (Strictness strictness : Strictness.values()) {
88+
maybeRemoveImport(strictness.runner);
89+
}
90+
maybeAddImport("org.mockito.quality.Strictness");
91+
maybeAddImport("org.mockito.junit.jupiter.MockitoSettings");
92+
}
93+
94+
private Optional<JavaTemplate> getTemplate(Strictness strictness, ExecutionContext ctx) {
95+
// MockitoExtension defaults to STRICT_STUBS, no need of explicit setting.
96+
if (strictness == Strictness.STRICT_STUBS) {
97+
return Optional.empty();
98+
}
99+
return Optional.of(JavaTemplate.builder("@MockitoSettings(strictness = Strictness." + strictness + ")")
100+
.imports("org.mockito.quality.Strictness", "org.mockito.junit.jupiter.MockitoSettings")
101+
.javaParser(JavaParser.fromJavaVersion()
102+
.classpathFromResources(ctx, "mockito-junit-jupiter-3.12", "mockito-core-3.12"))
103+
.build());
104+
}
105+
});
106+
}
107+
108+
private enum Strictness {
109+
LENIENT("org.mockito.junit.MockitoJUnitRunner.Silent"),
110+
STRICT_STUBS("org.mockito.junit.MockitoJUnitRunner.Strict"),
111+
WARN("org.mockito.junit.MockitoJUnitRunner");
112+
113+
final String runner;
114+
115+
Strictness(String runner) {
116+
this.runner = runner;
117+
}
118+
}
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (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+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
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+
package org.openrewrite.java.testing.mockito;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.params.ParameterizedTest;
20+
import org.junit.jupiter.params.provider.CsvSource;
21+
import org.openrewrite.DocumentExample;
22+
import org.openrewrite.InMemoryExecutionContext;
23+
import org.openrewrite.java.JavaParser;
24+
import org.openrewrite.test.RecipeSpec;
25+
import org.openrewrite.test.RewriteTest;
26+
27+
import static org.openrewrite.java.Assertions.java;
28+
29+
class MockitoJUnitRunnerToExtensionTest implements RewriteTest {
30+
31+
@Override
32+
public void defaults(RecipeSpec spec) {
33+
spec
34+
.parser(JavaParser.fromJavaVersion()
35+
.classpathFromResources(new InMemoryExecutionContext(), "junit-4", "mockito-core-3.12"))
36+
.recipe(new MockitoJUnitRunnerToExtension());
37+
}
38+
39+
@DocumentExample
40+
@ParameterizedTest
41+
@CsvSource({
42+
"MockitoJUnitRunner.Silent.class,Strictness.LENIENT",
43+
"MockitoJUnitRunner.class,Strictness.WARN"
44+
})
45+
void mockitoRunnerToExtension(String runnerName, String strictness) {
46+
//language=java
47+
rewriteRun(
48+
java(
49+
String.format("""
50+
import org.junit.runner.RunWith;
51+
import org.mockito.junit.MockitoJUnitRunner;
52+
53+
@RunWith(%s)
54+
public class ExternalAPIServiceTest {
55+
}
56+
""", runnerName),
57+
String.format("""
58+
import org.junit.jupiter.api.extension.ExtendWith;
59+
import org.mockito.junit.jupiter.MockitoExtension;
60+
import org.mockito.junit.jupiter.MockitoSettings;
61+
import org.mockito.quality.Strictness;
62+
63+
@MockitoSettings(strictness = %s)
64+
@ExtendWith(MockitoExtension.class)
65+
public class ExternalAPIServiceTest {
66+
}
67+
""", strictness)
68+
)
69+
);
70+
}
71+
72+
@Test
73+
void strictMockitoRunnerToExtension() {
74+
//language=java
75+
rewriteRun(
76+
java(
77+
"""
78+
import org.junit.runner.RunWith;
79+
import org.mockito.junit.MockitoJUnitRunner;
80+
81+
@RunWith(MockitoJUnitRunner.Strict.class)
82+
public class ExternalAPIServiceTest {
83+
}
84+
""",
85+
"""
86+
import org.junit.jupiter.api.extension.ExtendWith;
87+
import org.mockito.junit.jupiter.MockitoExtension;
88+
89+
@ExtendWith(MockitoExtension.class)
90+
public class ExternalAPIServiceTest {
91+
}
92+
"""
93+
)
94+
);
95+
}
96+
97+
@Test
98+
void noMockitoRunner() {
99+
//language=java
100+
rewriteRun(
101+
java(
102+
"""
103+
import org.junit.runner.RunWith;
104+
import org.junit.runners.Parameterized;
105+
106+
@RunWith(Parameterized.class)
107+
public class ExternalAPIServiceTest {
108+
}
109+
"""
110+
)
111+
);
112+
}
113+
}

0 commit comments

Comments
 (0)