Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5 add recipe to add testinstancelifecycleper class to junit test classes #6

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.github.mboegers.openrewrite.testngtojupiter;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.AnnotationMatcher;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;

import java.time.Duration;
import java.util.Comparator;

@Value
@EqualsAndHashCode(callSuper = false)
public class AddTestLifecyleToJUnitTests extends Recipe {

@Override
public String getDisplayName() {
return "Add @TestInstance(TestInstance.Lifecycle.PER_CLASS) to Jupiter tests";
}

@Override
public String getDescription() {
return "To align JUnit Jupiter behavior with the expected behavior coming from TestNG the annotation @TestInstance(TestInstance.Lifecycle.PER_CLASS) is needed.";
}

@Override
public Duration getEstimatedEffortPerOccurrence() {
return Duration.ofMinutes(5);
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new AddTestLifecyleToJUnitTests.AddLifecyleAnnotationVisitor();
}

static class AddLifecyleAnnotationVisitor extends JavaIsoVisitor<ExecutionContext> {

private static final AnnotationMatcher TEST_MATCHER = new AnnotationMatcher("@org.junit.jupiter.api.Test");
private static final AnnotationMatcher TEST_INSTANCE_MATCHER = new AnnotationMatcher("@org.junit.jupiter.api.TestInstance");

@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) {
classDecl = super.visitClassDeclaration(classDecl, executionContext);

boolean hasTestInstanceAnnotation = classDecl.getLeadingAnnotations().stream().anyMatch(TEST_INSTANCE_MATCHER::matches);

Boolean usesJUnit = getCursor().pollMessage("USES_JUNIT");
if (hasTestInstanceAnnotation || usesJUnit == null || !usesJUnit) {
return classDecl;
}

// transform TestNG @Test to Jupiter
var addAnnotationCoordinate = classDecl.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName));
var cursor = getCursor();
classDecl = JavaTemplate
.builder("@TestInstance(TestInstance.Lifecycle.PER_CLASS)")
.javaParser(JavaParser.fromJavaVersion().classpath("junit-jupiter-api"))
.imports("org.junit.jupiter.api.TestInstance")
.build()
.apply(cursor, addAnnotationCoordinate);

// update imports
maybeAddImport("org.junit.jupiter.api.TestInstance", false);

return classDecl;
}

@Override
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext executionContext) {
annotation = super.visitAnnotation(annotation, executionContext);

if (TEST_MATCHER.matches(annotation)) {
getCursor().dropParentUntil(J.ClassDeclaration.class::isInstance).putMessage("USES_JUNIT", true);
}

return annotation;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public String getDescription() {

@Override
public Duration getEstimatedEffortPerOccurrence() {
return Duration.ofMinutes(5);
return Duration.ofMinutes(3);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package io.github.mboegers.openrewrite.testngtojupiter;

import org.junit.jupiter.api.Test;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class AddTestLifecycleToJUnitTestsTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.parser(JavaParser.fromJavaVersion()
.logCompilationWarningsAndErrors(true)
.classpath("junit-jupiter-api", "testng"))
.recipe(new AddTestLifecyleToJUnitTests());
}

@Test
void addToJUnitTest() {
//language=java
rewriteRun(java(
"""
import org.junit.jupiter.api.Test;

class MyTest {
@Test
void test() {}
}
""", """
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTest {
@Test
void test() {}
}
"""));
}

@Test
void addToNestedJUnitTest() {
//language=java
rewriteRun(java(
"""
import org.junit.jupiter.api.Test;

class MyTest {
class InnerTest {
@Test
void test() {}
}

class OtherClass {}
}
""", """
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

class MyTest {
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class InnerTest {
@Test
void test() {}
}

class OtherClass {}
}
"""));
}

@Test
void doNetReaddToJUnitTest() {
//language=java
rewriteRun(java("""
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTest {
@Test
void test() {}
}
"""));
}

@Test
void doNetReaddToTestNGTest() {
//language=java
rewriteRun(java("""
import org.testng.annotations.Test;

class MyTest {
@Test
void test() {}
}
"""));
}
}
Loading