-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Recipe for migrating JUnit4 @RunWith(Parameterized.class) to the …
- Loading branch information
Showing
2 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
199 changes: 199 additions & 0 deletions
199
src/main/java/org/openrewrite/java/testing/junit5/ParameterizedRunnerToParameterized.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
package org.openrewrite.java.testing.junit5; | ||
|
||
import org.openrewrite.Cursor; | ||
import org.openrewrite.ExecutionContext; | ||
import org.openrewrite.Recipe; | ||
import org.openrewrite.TreeVisitor; | ||
import org.openrewrite.internal.ListUtils; | ||
import org.openrewrite.java.AnnotationMatcher; | ||
import org.openrewrite.java.JavaIsoVisitor; | ||
import org.openrewrite.java.JavaParser; | ||
import org.openrewrite.java.tree.*; | ||
import org.openrewrite.marker.Markers; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import static org.openrewrite.Tree.randomId; | ||
|
||
/** | ||
* Recipe for converting @RunWith(@Parameterized.class) to @ParameterizedTests with @MethodSource | ||
* <p> | ||
* 1. Remove `@RunWith(Parameterized.class)` | ||
* 2. Replace `@Test` with `@ParameterizedTest` having arguments from `@Parameters` method | ||
* 3. Add `@MethodSource(...)` with argument equal to `@Parameters` method name to each `@ParameterizedTest` | ||
* 4. Remove @Parameters annotation | ||
* 5. Change constructor to an initialization method having a void return type. | ||
* 6. For each `@ParameterizedTest` Insert statement to Invoke initialization method with test parameters | ||
* 7. Remove imports | ||
* org.junit.Test; | ||
* org.junit.runner.RunWith; | ||
* org.junit.runners.Parameterized; | ||
* org.junit.runners.Parameterized.Parameters; | ||
* 8. Add imports | ||
* org.junit.jupiter.params.ParameterizedTest; | ||
* org.junit.jupiter.params.provider.MethodSource; | ||
*/ | ||
public class ParameterizedRunnerToParameterized extends Recipe { | ||
|
||
private static final AnnotationMatcher RUN_WITH_PARAMETERS_ANNOTATION_MATCHER = new AnnotationMatcher("@org.junit.runner.RunWith(org.junit.runners.Parameterized.class)"); | ||
private static final AnnotationMatcher TEST_ANNOTATION_MATCHER = new AnnotationMatcher("@org.junit.Test"); | ||
private static final AnnotationMatcher PARAMETERS_MATCHER = new AnnotationMatcher("@org.junit.runners.Parameterized.Parameters"); | ||
private static final AnnotationMatcher PARAMETERIZED_TEST_ANNOTATION_MATCHER = new AnnotationMatcher("@org.junit.jupiter.params.ParameterizedTest"); | ||
|
||
private static final String PARAMETERIZED_TEST_ANNOTATION_PARAMETERS = "parameterizedTestParameters"; | ||
private static final String PARAMETERIZED_TEST_METHOD_PARAMETERS = "parameterizedTestMethodParameters"; | ||
private static final String METHOD_REFERENCE_NAME = "methodReferenceName"; | ||
private static final String INIT_METHOD_NAME = "initMethodName"; | ||
|
||
private static final ThreadLocal<JavaParser> PARAMETERIZED_TEMPLATE_PARSER = ThreadLocal.withInitial(() -> | ||
JavaParser.fromJavaVersion().build() | ||
); | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return "JUnit4 @RunWith(Parameterized.class) to JUnit Jupiter Parameterized Tests"; | ||
} | ||
|
||
@Override | ||
public String getDescription() { | ||
return "Convert JUnit4 Parameterized runner the JUnit Jupiter ParameterizedTest equivalent."; | ||
} | ||
|
||
@Override | ||
protected TreeVisitor<?, ExecutionContext> getVisitor() { | ||
return new ParameterizedRunnerVisitor(); | ||
} | ||
|
||
/** | ||
* Visitor for collecting Parameterized Test components and then scheduling the appropriate conversion visitor for the next visit | ||
*/ | ||
protected class ParameterizedRunnerVisitor extends JavaIsoVisitor<ExecutionContext> { | ||
|
||
@Override | ||
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) { | ||
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, executionContext); | ||
|
||
String methodReferenceName = getCursor().getMessage(METHOD_REFERENCE_NAME); | ||
String initMethodName = getCursor().getMessage(INIT_METHOD_NAME); | ||
List<Expression> testAnnotationParams = getCursor().getMessage(PARAMETERIZED_TEST_ANNOTATION_PARAMETERS); | ||
List<Statement> testMethodParams = getCursor().getMessage(PARAMETERIZED_TEST_METHOD_PARAMETERS); | ||
|
||
// Condition for converting to ParameterizedTest with MethodParams | ||
if (methodReferenceName != null && testMethodParams != null && initMethodName != null) { | ||
doAfterVisit(new ParameterizedTestWithMethodSourceVisitor(methodReferenceName, initMethodName, testAnnotationParams, testMethodParams)); | ||
} | ||
return cd; | ||
} | ||
|
||
@Override | ||
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) { | ||
J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext); | ||
Cursor classDeclCursor = getCursor().dropParentUntil(J.ClassDeclaration.class::isInstance); | ||
m = m.withLeadingAnnotations(ListUtils.map(m.getLeadingAnnotations(), anno -> { | ||
if (PARAMETERS_MATCHER.matches(anno)) { | ||
classDeclCursor.putMessage(PARAMETERIZED_TEST_ANNOTATION_PARAMETERS, anno.getArguments()); | ||
classDeclCursor.putMessage(METHOD_REFERENCE_NAME, method.getSimpleName()); | ||
} | ||
return anno; | ||
})); | ||
if (m.isConstructor()) { | ||
classDeclCursor.putMessage(PARAMETERIZED_TEST_METHOD_PARAMETERS, m.getParameters()); | ||
classDeclCursor.putMessage(INIT_METHOD_NAME, "init" + m.getSimpleName()); | ||
} | ||
return m; | ||
} | ||
|
||
/** | ||
* Visitor for converting Parameterized runner to Parameterized Tests with an associated MethodSource | ||
*/ | ||
protected class ParameterizedTestWithMethodSourceVisitor extends JavaIsoVisitor<ExecutionContext> { | ||
private final String methodReference; | ||
private final String initMethodName; | ||
private final List<Expression> parameterizedTestAnnotationParameters; | ||
private final List<Statement> parameterizedTestMethodParameters; | ||
private final String initStatementParams; | ||
private final String parameterizedTestAnnotationTemplate; | ||
|
||
public ParameterizedTestWithMethodSourceVisitor(String methodReference, String initMethodName, List<Expression> parameterizedTestAnnotationParameters, List<Statement> parameterizedTestMethodParameters) { | ||
this.methodReference = methodReference; | ||
this.initMethodName = initMethodName; | ||
this.parameterizedTestAnnotationParameters = parameterizedTestAnnotationParameters; | ||
this.parameterizedTestMethodParameters = parameterizedTestMethodParameters; | ||
this.initStatementParams = parameterizedTestMethodParameters.stream() | ||
.map(J.VariableDeclarations.class::cast) | ||
.map(n -> { | ||
return n.getVariables().get(0).getSimpleName(); | ||
}) | ||
.collect(Collectors.joining(", ")); | ||
this.parameterizedTestAnnotationTemplate = parameterizedTestAnnotationParameters != null ? "@ParameterizedTest(" + parameterizedTestAnnotationParameters.get(0).print() + ")" : "@ParameterizedTest"; | ||
} | ||
|
||
@Override | ||
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) { | ||
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, executionContext); | ||
// Remove @RunWith(Parameterized.class) annotation | ||
cd = cd.withLeadingAnnotations(ListUtils.map(cd.getLeadingAnnotations(), anno -> { | ||
if (RUN_WITH_PARAMETERS_ANNOTATION_MATCHER.matches(anno)) { | ||
return null; | ||
} | ||
return anno; | ||
})); | ||
|
||
// Update Imports | ||
maybeRemoveImport("org.junit.Test"); | ||
maybeRemoveImport("org.junit.runner.RunWith"); | ||
maybeRemoveImport("org.junit.runners.Parameterized"); | ||
maybeRemoveImport("org.junit.runners.Parameterized.Parameters"); | ||
maybeAddImport("org.junit.jupiter.params.ParameterizedTest"); | ||
maybeAddImport("org.junit.jupiter.params.provider.MethodSource"); | ||
return cd; | ||
} | ||
|
||
@Override | ||
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) { | ||
J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext); | ||
|
||
// Remove the @Parameters annotation | ||
m = m.withLeadingAnnotations(ListUtils.map(m.getLeadingAnnotations(), anno -> { | ||
if (PARAMETERS_MATCHER.matches(anno)) { | ||
return null; | ||
} | ||
return anno; | ||
})); | ||
|
||
// Replace the @Test with @ParameterizedTest | ||
m = m.withLeadingAnnotations(ListUtils.map(m.getLeadingAnnotations(), anno -> { | ||
if (TEST_ANNOTATION_MATCHER.matches(anno)) { | ||
anno = anno.withTemplate(template(parameterizedTestAnnotationTemplate) | ||
.javaParser(PARAMETERIZED_TEMPLATE_PARSER.get()) | ||
.imports("org.junit.jupiter.params.ParameterizedTest").build(), | ||
anno.getCoordinates().replace()); | ||
} | ||
return anno; | ||
})); | ||
|
||
// Add @MethodSource, insert test init statement, add test method parameters | ||
if (m.getLeadingAnnotations().stream().anyMatch(PARAMETERIZED_TEST_ANNOTATION_MATCHER::matches)) { | ||
m = m.withTemplate(template("@MethodSource(\"" + methodReference + "\")") | ||
.javaParser(PARAMETERIZED_TEMPLATE_PARSER.get()) | ||
.imports("org.junit.jupiter.params.provider.MethodSource").build(), | ||
m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); | ||
m = m.withTemplate(template(initMethodName + "(#{});") | ||
.javaParser(PARAMETERIZED_TEMPLATE_PARSER.get()) | ||
.build(), m.getBody().getStatements().get(0).getCoordinates().before(), initStatementParams); | ||
m = m.withParameters(parameterizedTestMethodParameters); | ||
} | ||
|
||
// Change constructor to test init method | ||
if (m.isConstructor()) { | ||
m = m.withName(m.getName().withName(initMethodName)); | ||
m = maybeAutoFormat(m, m.withReturnTypeExpression(new J.Primitive(randomId(), Space.EMPTY, Markers.EMPTY, JavaType.Primitive.Void)), | ||
executionContext, getCursor().dropParentUntil(J.class::isInstance)); | ||
} | ||
return m; | ||
} | ||
} | ||
} | ||
} |
158 changes: 158 additions & 0 deletions
158
...test/kotlin/org/openrewrite/java/testing/junit5/ParameterizedRunnerToParameterizedTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package org.openrewrite.java.testing.junit5 | ||
|
||
import org.junit.jupiter.api.Test | ||
import org.openrewrite.Recipe | ||
import org.openrewrite.java.JavaParser | ||
import org.openrewrite.java.JavaRecipeTest | ||
|
||
class ParameterizedRunnerToParameterizedTest : JavaRecipeTest { | ||
|
||
override val parser: JavaParser = JavaParser.fromJavaVersion() | ||
.classpath("junit") | ||
.build() | ||
override val recipe: Recipe | ||
get() = ParameterizedRunnerToParameterized() | ||
|
||
@Test | ||
fun parametersNameHasParameters() = assertChanged( | ||
before = """ | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.Parameterized; | ||
import org.junit.runners.Parameterized.Parameters; | ||
@RunWith(Parameterized.class) | ||
public class VetTests { | ||
private String firstName; | ||
private String lastName; | ||
private Integer id; | ||
public VetTests(String firstName, String lastName, Integer id) { | ||
this.firstName = firstName; | ||
this.lastName = lastName; | ||
this.id = id; | ||
} | ||
@Test | ||
public void testSerialization() { | ||
Vet vet = new Vet(); | ||
vet.setFirstName(firstName); | ||
vet.setLastName(lastName); | ||
vet.setId(id); | ||
} | ||
@Parameters(name="{index}: {0} {1} - {2}") | ||
public static List<Object[]> parameters() { | ||
return Arrays.asList( | ||
new Object[] { "Otis", "TheDog", 124 }, | ||
new Object[] { "Garfield", "TheBoss", 126 }); | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
public class VetTests { | ||
private String firstName; | ||
private String lastName; | ||
private Integer id; | ||
public void initVetTests(String firstName, String lastName, Integer id) { | ||
this.firstName = firstName; | ||
this.lastName = lastName; | ||
this.id = id; | ||
} | ||
@MethodSource("parameters") | ||
@ParameterizedTest(name = "{index}: {0} {1} - {2}") | ||
public void testSerialization(String firstName, String lastName, Integer id) { | ||
initVetTests(firstName, lastName, id); | ||
Vet vet = new Vet(); | ||
vet.setFirstName(firstName); | ||
vet.setLastName(lastName); | ||
vet.setId(id); | ||
} | ||
public static List<Object[]> parameters() { | ||
return Arrays.asList( | ||
new Object[] { "Otis", "TheDog", 124 }, | ||
new Object[] { "Garfield", "TheBoss", 126 }); | ||
} | ||
} | ||
""" | ||
) | ||
@Test | ||
fun parameterizedTestToParameterizedTestsWithMethodSource() = assertChanged( | ||
before = """ | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.Parameterized; | ||
import org.junit.runners.Parameterized.Parameters; | ||
@RunWith(Parameterized.class) | ||
public class VetTests { | ||
private String firstName; | ||
private String lastName; | ||
private Integer id; | ||
public VetTests(String firstName, String lastName, Integer id) { | ||
this.firstName = firstName; | ||
this.lastName = lastName; | ||
this.id = id; | ||
} | ||
@Test | ||
public void testSerialization() { | ||
Vet vet = new Vet(); | ||
vet.setFirstName(firstName); | ||
vet.setLastName(lastName); | ||
vet.setId(id); | ||
} | ||
@Parameters | ||
public static List<Object[]> parameters() { | ||
return Arrays.asList( | ||
new Object[] { "Otis", "TheDog", 124 }, | ||
new Object[] { "Garfield", "TheBoss", 126 }); | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
public class VetTests { | ||
private String firstName; | ||
private String lastName; | ||
private Integer id; | ||
public void initVetTests(String firstName, String lastName, Integer id) { | ||
this.firstName = firstName; | ||
this.lastName = lastName; | ||
this.id = id; | ||
} | ||
@MethodSource("parameters") | ||
@ParameterizedTest | ||
public void testSerialization(String firstName, String lastName, Integer id) { | ||
initVetTests(firstName, lastName, id); | ||
Vet vet = new Vet(); | ||
vet.setFirstName(firstName); | ||
vet.setLastName(lastName); | ||
vet.setId(id); | ||
} | ||
public static List<Object[]> parameters() { | ||
return Arrays.asList( | ||
new Object[] { "Otis", "TheDog", 124 }, | ||
new Object[] { "Garfield", "TheBoss", 126 }); | ||
} | ||
} | ||
""" | ||
) | ||
} |