-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented AssertSameToAssertThat visitor (#21)
Implemented visitor to convert JUnit's assertSame() to assertJ's assertThat().isSameAs()
- Loading branch information
1 parent
ca93760
commit 5ad7208
Showing
2 changed files
with
366 additions
and
0 deletions.
There are no files selected for viewing
138 changes: 138 additions & 0 deletions
138
src/main/java/org/openrewrite/java/testing/junitassertj/AssertSameToAssertThat.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,138 @@ | ||
/* | ||
* Copyright 2020 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.openrewrite.java.testing.junitassertj; | ||
|
||
import org.openrewrite.AutoConfigure; | ||
import org.openrewrite.java.*; | ||
import org.openrewrite.java.tree.*; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import static org.openrewrite.Formatting.EMPTY; | ||
import static org.openrewrite.Formatting.format; | ||
import static org.openrewrite.Tree.randomId; | ||
import static org.openrewrite.java.tree.MethodTypeBuilder.newMethodType; | ||
|
||
/** | ||
* This is a refactoring visitor that will convert JUnit-style assertSame() to assertJ's assertThat().isSameAs(). | ||
* | ||
* This visitor only supports the migration of the following JUnit 5 assertSame() methods: | ||
* | ||
* <PRE> | ||
* assertSame(Object expected, Object actual) -> assertThat(actual).isSameAs(expected) | ||
* assertSame(Object expected, Object actual, String message) -> assertThat(actual).as(message).isSameAs(expected) | ||
* assertSame(Object expected, Object actual, Supplier<String> messageSupplier) -> assertThat(actual).withFailMessage(messageSupplier).isSameAs(expected); | ||
* </PRE> | ||
*/ | ||
@AutoConfigure | ||
public class AssertSameToAssertThat extends JavaIsoRefactorVisitor { | ||
|
||
private static final String JUNIT_QUALIFIED_ASSERTIONS_CLASS_NAME = "org.junit.jupiter.api.Assertions"; | ||
private static final String ASSERTJ_QUALIFIED_ASSERTIONS_CLASS_NAME = "org.assertj.core.api.Assertions"; | ||
private static final String ASSERTJ_ASSERT_THAT_METHOD_NAME = "assertThat"; | ||
|
||
/** | ||
* This matcher finds the junit methods that will be migrated by this visitor. | ||
*/ | ||
private static final MethodMatcher JUNIT_ASSERT_SAME_MATCHER = new MethodMatcher( | ||
JUNIT_QUALIFIED_ASSERTIONS_CLASS_NAME + " assertSame(..)" | ||
); | ||
|
||
private static final JavaType.Method assertThatMethodType = newMethodType() | ||
.declaringClass(ASSERTJ_QUALIFIED_ASSERTIONS_CLASS_NAME) | ||
.flags(Flag.Public, Flag.Static) | ||
.returnType("org.assertj.core.api.AbstractBooleanAssert") | ||
.name(ASSERTJ_ASSERT_THAT_METHOD_NAME) | ||
.parameter(JavaType.Primitive.Boolean, "arg1") | ||
.build(); | ||
|
||
@Override | ||
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method) { | ||
J.MethodInvocation original = super.visitMethodInvocation(method); | ||
if (!JUNIT_ASSERT_SAME_MATCHER.matches(method)) { | ||
return original; | ||
} | ||
|
||
List<Expression> originalArgs = original.getArgs().getArgs(); | ||
Expression expected = originalArgs.get(0); | ||
Expression actual = originalArgs.get(1); | ||
Expression message = originalArgs.size() == 3 ? originalArgs.get(2) : null; | ||
|
||
// This creates the `assertThat(<EXPRESSION>)` method invocation. Without the type information for this invocation, | ||
// the AddImport visitor won't add a static import for "assertThat" (assuming it doesn't already exist) | ||
// because it won't be able to find a reference to the type. | ||
J.MethodInvocation assertSelect = new J.MethodInvocation( | ||
randomId(), | ||
null, | ||
null, | ||
J.Ident.build(randomId(), ASSERTJ_ASSERT_THAT_METHOD_NAME, JavaType.Primitive.Void, EMPTY), | ||
new J.MethodInvocation.Arguments( | ||
randomId(), | ||
Collections.singletonList(actual.withPrefix("")), | ||
EMPTY | ||
), | ||
assertThatMethodType, | ||
EMPTY | ||
); | ||
|
||
// If the assertSame is the three-argument variant, we need to maintain the message via a chained method | ||
// call to "as"/"withFailMessage". The message may be a String or Supplier<String>. | ||
if (message != null) { | ||
// In assertJ the "as" method has a more informative error message, but doesn't accept String suppliers | ||
// so we're using "as" if the message is a string and "withFailMessage" if it is a supplier. | ||
String messageAs = TypeUtils.isString(message.getType())? "as" : "withFailMessage"; | ||
|
||
assertSelect = new J.MethodInvocation( | ||
randomId(), | ||
assertSelect, // assertThat is the select for this method. | ||
null, | ||
J.Ident.build(randomId(), messageAs, null, EMPTY), | ||
new J.MethodInvocation.Arguments( | ||
randomId(), | ||
Collections.singletonList(message.withPrefix("")), | ||
EMPTY | ||
), | ||
null, | ||
EMPTY | ||
); | ||
} | ||
|
||
// This will always return the "isSameAs()" method using assertSelect as the select. | ||
J.MethodInvocation replacement = new J.MethodInvocation( | ||
randomId(), | ||
assertSelect, | ||
null, | ||
J.Ident.build(randomId(), "isSameAs", JavaType.Primitive.Boolean, EMPTY), | ||
new J.MethodInvocation.Arguments( | ||
randomId(), | ||
Collections.singletonList(expected.withPrefix("")), | ||
EMPTY | ||
), | ||
null, | ||
format("\n") | ||
); | ||
// Remove import for "org.junit.jupiter.api.Assertions" if no longer used. | ||
maybeRemoveImport(JUNIT_QUALIFIED_ASSERTIONS_CLASS_NAME); | ||
|
||
// Make sure there is a static import for "org.assertj.core.api.Assertions.assertThat". | ||
maybeAddImport(ASSERTJ_QUALIFIED_ASSERTIONS_CLASS_NAME, ASSERTJ_ASSERT_THAT_METHOD_NAME); | ||
|
||
// Format the replacement method invocation in the context of where it is called. | ||
andThen(new AutoFormat(replacement)); | ||
return replacement; | ||
} | ||
} |
228 changes: 228 additions & 0 deletions
228
src/test/kotlin/org/openrewrite/java/testing/junitassertj/AssertSameToAssertThatTest.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,228 @@ | ||
/* | ||
* Copyright 2020 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.openrewrite.java.testing.junitassertj | ||
|
||
import org.junit.jupiter.api.Test | ||
import org.openrewrite.Parser | ||
import org.openrewrite.RefactorVisitor | ||
import org.openrewrite.RefactorVisitorTestForParser | ||
import org.openrewrite.java.JavaParser | ||
import org.openrewrite.java.tree.J | ||
|
||
class AssertSameToAssertThatTest : RefactorVisitorTestForParser<J.CompilationUnit> { | ||
override val parser: Parser<J.CompilationUnit> = JavaParser.fromJavaVersion() | ||
.classpath("junit-jupiter-api", "assertj-core", "apiguardian-api") | ||
.build() | ||
|
||
override val visitors: Iterable<RefactorVisitor<*>> = listOf(AssertSameToAssertThat()) | ||
|
||
@Test | ||
fun singleStaticMethodNoMessage() = assertRefactored( | ||
before = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.junit.jupiter.api.Assertions.assertSame; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "String"; | ||
assertSame(notification(), str); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "String"; | ||
assertThat(str).isSameAs(notification()); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""" | ||
) | ||
|
||
@Test | ||
fun singleStaticMethodWithMessageString() = assertRefactored( | ||
before = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.junit.jupiter.api.Assertions.assertSame; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertSame(notification(), str, "Should be the same"); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertThat(str).as("Should be the same").isSameAs(notification()); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""" | ||
) | ||
|
||
@Test | ||
fun singleStaticMethodWithMessageSupplier() = assertRefactored( | ||
before = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.junit.jupiter.api.Assertions.assertSame; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertSame(notification(), str, () -> "Should be the same"); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertThat(str).withFailMessage(() -> "Should be the same").isSameAs(notification()); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""" | ||
) | ||
|
||
@Test | ||
fun inlineReference() = assertRefactored( | ||
before = """ | ||
import org.junit.jupiter.api.Test; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
org.junit.jupiter.api.Assertions.assertSame(notification(), str); | ||
org.junit.jupiter.api.Assertions.assertSame(notification(), str, "Should be the same"); | ||
org.junit.jupiter.api.Assertions.assertSame(notification(), str, () -> "Should be the same"); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertThat(str).isSameAs(notification()); | ||
assertThat(str).as("Should be the same").isSameAs(notification()); | ||
assertThat(str).withFailMessage(() -> "Should be the same").isSameAs(notification()); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""" | ||
) | ||
|
||
@Test | ||
fun mixedReferences() = assertRefactored( | ||
before = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.*; | ||
import static org.junit.jupiter.api.Assertions.assertSame; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertSame(notification(), str); | ||
org.junit.jupiter.api.Assertions.assertSame(notification(), str, "Should be the same"); | ||
assertSame(notification(), str, () -> "Should be the same"); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""", | ||
after = """ | ||
import org.junit.jupiter.api.Test; | ||
import static org.assertj.core.api.Assertions.*; | ||
public class A { | ||
@Test | ||
public void test() { | ||
String str = "string"; | ||
assertThat(str).isSameAs(notification()); | ||
assertThat(str).as("Should be the same").isSameAs(notification()); | ||
assertThat(str).withFailMessage(() -> "Should be the same").isSameAs(notification()); | ||
} | ||
private String notification() { | ||
return "String"; | ||
} | ||
} | ||
""" | ||
) | ||
} |