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

Jmockit mockito full verifications and Refactor tests #617

Merged
merged 11 commits into from
Oct 22, 2024
Next Next commit
Migrate JMockit FullVerifications to Mockito
shivanisky committed Oct 13, 2024
commit 2d0ae1033af73ee7608e76c660553806788727a5
Original file line number Diff line number Diff line change
@@ -29,13 +29,14 @@
import java.util.ArrayList;
import java.util.List;

import static org.openrewrite.java.testing.jmockit.JMockitBlockType.FullVerifications;
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.NonStrictExpectations;
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.Verifications;

class JMockitBlockRewriter {

private static final String WHEN_TEMPLATE_PREFIX = "when(#{any()}).";
private static final String VERIFY_TEMPLATE_PREFIX = "verify(#{any()}";
private static final String VERIFY_NO_INTERACTIONS_TEMPLATE_PREFIX = "verifyNoMoreInteractions(";
private static final String LENIENT_TEMPLATE_PREFIX = "lenient().";

private static final String RETURN_TEMPLATE_PREFIX = "thenReturn(";
@@ -95,14 +96,21 @@ J.Block rewriteMethodBody() {

// iterate over the statements and build a list of grouped method invocations and related statements eg times
List<List<Statement>> methodInvocationsToRewrite = new ArrayList<>();
List<J.Identifier> mocks = new ArrayList<>();
int methodInvocationIdx = -1;
for (Statement jmockitBlockStatement : jmockitBlock.getStatements()) {
if (jmockitBlockStatement instanceof J.MethodInvocation) {
// ensure it's not a returns statement, we add that later to related statements
J.MethodInvocation invocation = (J.MethodInvocation) jmockitBlockStatement;
if (invocation.getSelect() != null && !invocation.getName().getSimpleName().equals("returns")) {
methodInvocationIdx++;
methodInvocationsToRewrite.add(new ArrayList<>());
J.Identifier object = (J.Identifier) invocation.getSelect();
if (object != null) {
// ensure it's not a returns statement, we add that later to related statements
if (!invocation.getName().getSimpleName().equals("returns")) {
methodInvocationIdx++;
methodInvocationsToRewrite.add(new ArrayList<>());
}
if (mocks.stream().noneMatch(mock -> mock.getType().equals(object.getType()) && mock.getSimpleName().equals(object.getSimpleName()))) {
mocks.add(object);
}
}
}

@@ -118,6 +126,10 @@ J.Block rewriteMethodBody() {

// now rewrite
methodInvocationsToRewrite.forEach(this::rewriteMethodInvocation);

if (blockType == FullVerifications) {
rewriteFullVerifications(new ArrayList<>(mocks));
}
return methodBody;
}

@@ -136,7 +148,7 @@ private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
rewriteResult(invocation, mockInvocationResults.getResults(), hasTimes);
}

if (!hasResults && !hasTimes && (this.blockType == JMockitBlockType.Expectations || this.blockType == Verifications)) {
if (!hasResults && !hasTimes && (this.blockType == JMockitBlockType.Expectations || this.blockType.isVerifications())) {
rewriteVerify(invocation, null, "");
return;
}
@@ -171,7 +183,7 @@ private void rewriteResult(J.MethodInvocation invocation, List<Expression> resul
List<Object> templateParams = new ArrayList<>();
templateParams.add(invocation);
templateParams.addAll(results);
this.rewriteFailed = !rewriteTemplate(template, templateParams, nextStatementCoordinates);
rewriteTemplate(template, templateParams, nextStatementCoordinates);
if (this.rewriteFailed) {
return;
}
@@ -199,19 +211,19 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
templateParams.add(invocation.getName().getSimpleName());
String verifyTemplate = getVerifyTemplate(invocation.getArguments(), verificationMode, templateParams);
JavaCoordinates verifyCoordinates;
if (this.blockType == Verifications) {
if (this.blockType.isVerifications()) {
// for Verifications, replace the Verifications block
verifyCoordinates = nextStatementCoordinates;
} else {
// for Expectations put the verify at the end of the method
verifyCoordinates = methodBody.getCoordinates().lastStatement();
}
this.rewriteFailed = !rewriteTemplate(verifyTemplate, templateParams, verifyCoordinates);
rewriteTemplate(verifyTemplate, templateParams, verifyCoordinates);
if (this.rewriteFailed) {
return;
}

if (this.blockType == Verifications) {
if (this.blockType.isVerifications()) {
setNextStatementCoordinates(++numStatementsAdded); // for Expectations, verify statements added to end of method
}

@@ -223,6 +235,20 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
}
}

private void rewriteFullVerifications(List<Object> mocks) {
StringBuilder sb = new StringBuilder(VERIFY_NO_INTERACTIONS_TEMPLATE_PREFIX);
for (int i = 0; i < mocks.size(); i++) {
sb.append(ANY_TEMPLATE_FIELD).append(","); // verifyNoMoreInteractions(mock1, mock2 ...
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
rewriteTemplate(sb.toString(), mocks, nextStatementCoordinates);
if (!this.rewriteFailed) {
setNextStatementCoordinates(++numStatementsAdded);
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "verifyNoMoreInteractions", false);
}
}

private void setNextStatementCoordinates(int numStatementsAdded) {
if (numStatementsAdded <= 0 && bodyStatementIndex == 0) {
nextStatementCoordinates = methodBody.getCoordinates().firstStatement();
@@ -240,7 +266,7 @@ private void setNextStatementCoordinates(int numStatementsAdded) {
this.nextStatementCoordinates = this.methodBody.getStatements().get(lastStatementIdx).getCoordinates().after();
}

private boolean rewriteTemplate(String template, List<Object> templateParams, JavaCoordinates
private void rewriteTemplate(String template, List<Object> templateParams, JavaCoordinates
rewriteCoords) {
int numStatementsBefore = methodBody.getStatements().size();
methodBody = JavaTemplate.builder(template)
@@ -252,7 +278,7 @@ private boolean rewriteTemplate(String template, List<Object> templateParams, Ja
rewriteCoords,
templateParams.toArray()
);
return methodBody.getStatements().size() > numStatementsBefore;
this.rewriteFailed = methodBody.getStatements().size() <= numStatementsBefore;
}

private @Nullable String getWhenTemplate(List<Expression> results, boolean lenient) {
Original file line number Diff line number Diff line change
@@ -26,31 +26,34 @@
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Statement;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static org.openrewrite.java.testing.jmockit.JMockitBlockType.*;
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.getSupportedTypesStr;
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.values;

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

private static final String SUPPORTED_TYPES = getSupportedTypesStr();

@Override
public String getDisplayName() {
return "Rewrite JMockit Expectations, Verifications and NonStrictExpectations";
return "Rewrite JMockit " + SUPPORTED_TYPES;
}

@Override
public String getDescription() {
return "Rewrites JMockit `Expectations, Verifications and NonStrictExpectations` blocks to Mockito statements.";
return "Rewrites JMockit `" + SUPPORTED_TYPES + "` blocks to Mockito statements.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(Preconditions.or(
new UsesType<>(Expectations.getFqn(), false),
new UsesType<>(Verifications.getFqn(), false),
new UsesType<>(NonStrictExpectations.getFqn(), false)), new RewriteJMockitBlockVisitor());
@SuppressWarnings("rawtypes")
UsesType[] usesTypes = Arrays.stream(values()).map(blockType -> new UsesType<>(blockType.getFqn(), false)).toArray(UsesType[]::new);
return Preconditions.check(Preconditions.or(usesTypes), new RewriteJMockitBlockVisitor());
}

private static class RewriteJMockitBlockVisitor extends JavaIsoVisitor<ExecutionContext> {
Original file line number Diff line number Diff line change
@@ -17,16 +17,30 @@

import lombok.Getter;

import java.util.Arrays;

@Getter
enum JMockitBlockType {

Expectations,
NonStrictExpectations,
Verifications,
NonStrictExpectations;
FullVerifications;

private final String fqn;

JMockitBlockType() {
this.fqn = "mockit." + this.name();
}

public boolean isVerifications() {
String blockType = this.name();
return blockType.equals(Verifications.name()) || blockType.equals(FullVerifications.name());
}

public static String getSupportedTypesStr() {
StringBuilder sb = new StringBuilder();
Arrays.stream(values()).forEach(value -> sb.append(value).append(", "));
return sb.substring(0, sb.length() - 2);
}
}
Loading