Implemented AssertThatStringIsEmpty.

This commit is contained in:
Chris Hodges 2019-03-24 09:15:07 +01:00
parent 8051511524
commit 5fa61a3004
12 changed files with 166 additions and 69 deletions

View File

@ -4,12 +4,25 @@ Cajon is an IntelliJ Plugin for shortening and optimizing AssertJ assertions.
## Why?
First, code is easier to read, when is concise and reflects the intention clearly.
AssertJ has plenty of different convenience methods that make describing the various intentions.
First, code is easier to read, when it is concise and reflects the intention clearly.
AssertJ has plenty of different convenience methods that describing various intentions precisely.
Why write longer, more complex code that can be expressed in brevity?
Second, AssertJ is able to output more meaningful descriptions when an assertion fails.
This makes finding bugs and fixing failed tests easier.
This makes finding bugs and fixing failed tests more efficient.
For example:
> assertThat(collection.size()).isEqualTo(5);
If the collection has more or less than 5 elements, the assertion will fail, but will not
tell you about the contents, making it hard to guess what went wrong.
Instead, if you wrote the same assertion the following way:
> assertThat(collection).hasSize(5);
Then AssertJ would tell you the contents of the collection on failure.
## Implemented
@ -22,11 +35,11 @@ This makes finding bugs and fixing failed tests easier.
- AssertThatBooleanIsTrueOrFalse
> from: assertThat(booleanValue).isEqualTo(true/false/Boolean.TRUE/Boolean.FALSE);
> to: assertThat(booleanValue).isTrue()/isFalse();
## TODO
- AssertThatStringIsEmpty
> from: assertThat(string).isEqualTo("")
> to: assertThat(string).isEmpty();
## TODO
- AssertThatArrayHasLiteralSize
> from: assertThat(array.length).isEqualTo(literal); literal > 0
> to: assertThat(array).hasSize(literal);
@ -62,3 +75,6 @@ This makes finding bugs and fixing failed tests easier.
> from: assertThat(iterable.size()).isLessThan(1);
> from: assertThat(iterable).hasSize(0);
> to: assertThat(iterable).isEmpty();
- JUnit Assertion to AssertJ
- AssertThatGuavaOptionalContains

View File

@ -9,6 +9,7 @@ open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() {
companion object {
const val ABSTRACT_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractAssert"
const val ABSTRACT_BOOLEAN_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractBooleanAssert"
const val ABSTRACT_STRING_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractStringAssert"
const val IS_EQUAL_TO = "isEqualTo"
const val IS_NOT_EQUAL_TO = "isNotEqualTo"

View File

@ -1,13 +1,11 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.util.TypeConversionUtil
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls
class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() {
@ -63,34 +61,17 @@ class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() {
}
}
val expectedResult = result as? Boolean ?: return
val description =
if (flippedBooleanTest) QUICKFIX_DESCRIPTION_NOT_IS_TRUE else QUICKFIX_DESCRIPTION_IS_TRUE
val replacementMethod = if (expectedResult xor flippedBooleanTest) "isTrue()" else "isFalse()"
holder.registerProblem(
expression,
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceWithIsNullQuickFix(
expectedResult xor flippedBooleanTest,
if (flippedBooleanTest) QUICKFIX_DESCRIPTION_NOT_IS_TRUE else QUICKFIX_DESCRIPTION_IS_TRUE
)
ReplaceSimpleMethodCallQuickFix(description, replacementMethod)
)
}
}
}
private class ReplaceWithIsNullQuickFix(val expectedResult: Boolean, val quickfixName: String) : LocalQuickFix {
override fun getFamilyName() = quickfixName
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.startElement
val factory = JavaPsiFacade.getElementFactory(element.project)
val methodCallExpression = element as? PsiMethodCallExpression ?: return
val oldQualifier = methodCallExpression.methodExpression.qualifierExpression ?: return
val methodName = if (expectedResult) "isTrue()" else "isFalse()"
val isNullExpression =
factory.createExpressionFromText("a.$methodName", null) as PsiMethodCallExpression
isNullExpression.methodExpression.qualifierExpression!!.replace(oldQualifier)
element.replace(isNullExpression)
}
}
}

View File

@ -1,12 +1,13 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiType
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls
class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() {
@ -38,25 +39,10 @@ class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() {
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceWithIsNotNullQuickFix()
ReplaceSimpleMethodCallQuickFix(QUICKFIX_DESCRIPTION, "isNotNull()")
)
}
}
}
}
private class ReplaceWithIsNotNullQuickFix : LocalQuickFix {
override fun getFamilyName() = QUICKFIX_DESCRIPTION
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.startElement
val factory = JavaPsiFacade.getElementFactory(element.project)
val methodCallExpression = element as? PsiMethodCallExpression ?: return
val oldQualifier = methodCallExpression.methodExpression.qualifierExpression ?: return
val isNotNullExpression =
factory.createExpressionFromText("a.isNotNull()", null) as PsiMethodCallExpression
isNotNullExpression.methodExpression.qualifierExpression!!.replace(oldQualifier)
element.replace(isNotNullExpression)
}
}
}

View File

@ -1,12 +1,13 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiType
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls
class AssertThatObjectIsNullInspection : AbstractAssertJInspection() {
@ -38,25 +39,10 @@ class AssertThatObjectIsNullInspection : AbstractAssertJInspection() {
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceWithIsNullQuickFix()
ReplaceSimpleMethodCallQuickFix(QUICKFIX_DESCRIPTION, "isNull()")
)
}
}
}
}
private class ReplaceWithIsNullQuickFix : LocalQuickFix {
override fun getFamilyName() = QUICKFIX_DESCRIPTION
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.startElement
val factory = JavaPsiFacade.getElementFactory(element.project)
val methodCallExpression = element as? PsiMethodCallExpression ?: return
val oldQualifier = methodCallExpression.methodExpression.qualifierExpression ?: return
val isNullExpression =
factory.createExpressionFromText("a.isNull()", null) as PsiMethodCallExpression
isNullExpression.methodExpression.qualifierExpression!!.replace(oldQualifier)
element.replace(isNullExpression)
}
}
}

View File

@ -0,0 +1,51 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiLiteralExpression
import com.intellij.psi.PsiMethodCallExpression
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls
class AssertThatStringIsEmptyInspection : AbstractAssertJInspection() {
companion object {
@NonNls
private val DISPLAY_NAME = "Asserting an empty string"
@NonNls
private val INSPECTION_MESSAGE = "isEqualTo(\"\") can be simplified to isEmpty()"
@NonNls
private val QUICKFIX_DESCRIPTION = "Replace isEqualTo(\"\") with isEmpty()"
}
override fun getDisplayName() = DISPLAY_NAME
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : JavaElementVisitor() {
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
super.visitMethodCallExpression(expression)
if (!IS_EQUAL_TO_OBJECT.test(expression)) {
return
}
val psiExpression = expression.argumentList.expressions[0] as? PsiLiteralExpression ?: return
if (psiExpression.value == "") {
holder.registerProblem(
expression,
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceSimpleMethodCallQuickFix(QUICKFIX_DESCRIPTION, "isEmpty()")
)
}
}
}
}
}

View File

@ -0,0 +1,23 @@
package de.platon42.intellij.plugins.cajon.quickfixes
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiMethodCallExpression
class ReplaceSimpleMethodCallQuickFix(private val description: String, private val replacementMethod: String) :
LocalQuickFix {
override fun getFamilyName() = description
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.startElement
val factory = JavaPsiFacade.getElementFactory(element.project)
val methodCallExpression = element as? PsiMethodCallExpression ?: return
val oldQualifier = methodCallExpression.methodExpression.qualifierExpression ?: return
val isEmptyExpression =
factory.createExpressionFromText("a.$replacementMethod", null) as PsiMethodCallExpression
isEmptyExpression.methodExpression.qualifierExpression!!.replace(oldQualifier)
element.replace(isEmptyExpression)
}
}

View File

@ -25,6 +25,8 @@
<localInspection groupPath="Java" shortName="AssertThatBooleanIsTrueOrFalse" enabledByDefault="true"
level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatBooleanIsTrueOrFalseInspection"/>
<localInspection groupPath="Java" shortName="AssertThatStringIsEmpty" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatStringIsEmptyInspection"/>
</extensions>
<actions>

View File

@ -1,5 +1,7 @@
package de.platon42.intellij.playground;
import org.assertj.core.api.AbstractStringAssert;
import java.util.ArrayList;
import static org.assertj.core.api.Assertions.assertThat;
@ -48,4 +50,10 @@ public class Playground {
assertThat("").isEqualTo(Boolean.TRUE);
}
private void stringIsEmpty() {
String foo = "bar";
AbstractStringAssert<?> abstractStringAssert = assertThat(foo);
abstractStringAssert.isEqualTo("");
}
}

View File

@ -0,0 +1,21 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataSubPath
import de.platon42.intellij.plugins.cajon.AbstractCajonTest
import org.junit.jupiter.api.Test
internal class AssertThatStringIsEmptyInspectionTest : AbstractCajonTest() {
@Test
@TestDataSubPath("inspections/StringIsEmpty")
internal fun assertThat_with_isEqualTo_emptyString_can_use_isEmpty(@MyFixture myFixture: JavaCodeInsightTestFixture) {
runTest {
myFixture.enableInspections(AssertThatStringIsEmptyInspection::class.java)
myFixture.configureByFile("StringIsEmptyBefore.java")
executeQuickFixes(myFixture, Regex("Replace is.*"), 1)
myFixture.checkResultByFile("StringIsEmptyAfter.java")
}
}
}

View File

@ -0,0 +1,11 @@
import static org.assertj.core.api.Assertions.assertThat;
public class StringIsEmpty {
private void stringIsEmpty() {
String string = "string";
assertThat(string).isEqualTo("foo");
assertThat(string).isEmpty();
}
}

View File

@ -0,0 +1,11 @@
import static org.assertj.core.api.Assertions.assertThat;
public class StringIsEmpty {
private void stringIsEmpty() {
String string = "string";
assertThat(string).isEqualTo("foo");
assertThat(string).isEqualTo("");
}
}