TestExpectedJUnitPluginV2.java
/*******************************************************************************
* Copyright (c) 2025 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.jdt.internal.corext.fix.helper;
import static org.sandbox.jdt.internal.corext.fix.helper.lib.JUnitConstants.*;
import java.util.List;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.text.edits.TextEditGroup;
import org.sandbox.jdt.internal.corext.fix.helper.lib.JunitHolder;
import org.sandbox.jdt.internal.corext.fix.helper.lib.TriggerPatternCleanupPlugin;
import org.sandbox.jdt.triggerpattern.api.Pattern;
import org.sandbox.jdt.triggerpattern.api.PatternKind;
/**
* Plugin to migrate @Test(expected=Exception.class) to assertThrows().
* Demonstrates TriggerPattern with placeholder bindings.
*/
public class TestExpectedJUnitPluginV2 extends TriggerPatternCleanupPlugin {
@Override
protected List<Pattern> getPatterns() {
return List.of(
// Matches @Test(expected=SomeException.class)
new Pattern("@Test(expected=$exceptionType)", PatternKind.ANNOTATION, ORG_JUNIT_TEST)
);
}
@Override
protected void process2Rewrite(TextEditGroup group, ASTRewrite rewriter, AST ast,
ImportRewrite importRewriter, JunitHolder junitHolder) {
Annotation testAnnotation = junitHolder.getAnnotation();
// Get the exception type from placeholder binding
TypeLiteral exceptionType = (TypeLiteral) junitHolder.getBinding("$exceptionType");
if (exceptionType == null) {
return;
}
// Get the method declaration
MethodDeclaration method = ASTNodes.getParent(testAnnotation, MethodDeclaration.class);
if (method == null) {
return;
}
Block methodBody = method.getBody();
if (methodBody == null) {
return;
}
@SuppressWarnings("unchecked")
List<Statement> statements = methodBody.statements();
// Create assertThrows method invocation
MethodInvocation assertThrowsCall = ast.newMethodInvocation();
assertThrowsCall.setName(ast.newSimpleName(METHOD_ASSERT_THROWS));
// Add the exception class as the first argument
TypeLiteral exceptionClass = (TypeLiteral) ASTNodes.copySubtree(ast, exceptionType);
assertThrowsCall.arguments().add(exceptionClass);
// Create lambda expression for the method body
LambdaExpression lambda = ast.newLambdaExpression();
lambda.setParentheses(true);
Block lambdaBody = ast.newBlock();
// Copy all statements from the original method body into the lambda
for (Statement stmt : statements) {
Statement copiedStmt = (Statement) ASTNodes.copySubtree(ast, stmt);
lambdaBody.statements().add(copiedStmt);
}
lambda.setBody(lambdaBody);
assertThrowsCall.arguments().add(lambda);
// Create the new expression statement with assertThrows
ExpressionStatement assertThrowsStatement = ast.newExpressionStatement(assertThrowsCall);
// Remove all existing statements from the method body
for (int i = statements.size() - 1; i >= 0; i--) {
rewriter.remove(statements.get(i), group);
}
// Add the assertThrows statement as the only statement in the method
rewriter.getListRewrite(methodBody, Block.STATEMENTS_PROPERTY).insertLast(assertThrowsStatement, group);
// Replace @Test(expected=...) with @Test
MarkerAnnotation newAnnotation = ast.newMarkerAnnotation();
newAnnotation.setTypeName(ast.newSimpleName(ANNOTATION_TEST));
ASTNodes.replaceButKeepComment(rewriter, testAnnotation, newAnnotation, group);
// Update imports
importRewriter.removeImport(ORG_JUNIT_TEST);
importRewriter.addImport(ORG_JUNIT_JUPITER_TEST);
importRewriter.addStaticImport(ORG_JUNIT_JUPITER_API_ASSERTIONS, METHOD_ASSERT_THROWS, false);
}
@Override
public String getPreview(boolean afterRefactoring) {
if (afterRefactoring) {
return """
@Test
public void testException() {
assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException();
});
}
"""; //$NON-NLS-1$
}
return """
@Test(expected = IllegalArgumentException.class)
public void testException() {
throw new IllegalArgumentException();
}
"""; //$NON-NLS-1$
}
@Override
public String toString() {
return "Test(expected) (TriggerPattern)"; //$NON-NLS-1$
}
}