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

Migrate Jmockit VerificationsInOrder to Mockito #632

Merged
merged 18 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
Expand Up @@ -166,7 +166,7 @@ private Expression applyArgumentTemplate(Expression methodArgument, String argum
List<Object> templateParams) {
visitor.maybeAddImport("org.mockito.Mockito", argumentMatcher);
return JavaTemplate.builder(template)
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
.javaParser(JMockitUtils.getJavaParser(ctx))
.staticImports("org.mockito.Mockito." + argumentMatcher)
.build()
.apply(
Expand Down Expand Up @@ -232,7 +232,7 @@ private Expression rewriteAnyWithClassParameterToArgumentMatcher(Expression meth
}

private Expression applyArrayClassArgumentTemplate(Expression methodArgument, JavaType elementType) {
String newArrayElementClassName = "";
String newArrayElementClassName;
if (elementType instanceof JavaType.FullyQualified) {
newArrayElementClassName = ((JavaType.FullyQualified) elementType).getClassName();
} else if (elementType instanceof JavaType.Primitive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.search.UsesType;
Expand All @@ -32,6 +31,8 @@
import java.util.ArrayList;
import java.util.List;

import static org.openrewrite.java.testing.jmockit.JMockitUtils.getJavaParser;

@EqualsAndHashCode(callSuper = false)
public class JMockitAnnotatedArgumentToMockito extends Recipe {
@Override
Expand Down Expand Up @@ -82,7 +83,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDecl
// Add mocked parameters as statements to the method declaration
if (!mockedParameter.isEmpty()) {
JavaTemplate addStatementsTemplate = JavaTemplate.builder("#{} #{} = Mockito.mock(#{}.class);\n")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
.javaParser(getJavaParser(ctx))
.imports("org.mockito.Mockito")
.contextSensitive()
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,24 @@
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.*;
import static org.openrewrite.java.testing.jmockit.JMockitUtils.MOCKITO_ALL_IMPORT;
import static org.openrewrite.java.testing.jmockit.JMockitUtils.getJavaParser;

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 VERIFY_IN_ORDER_TEMPLATE_PREFIX_1 = "InOrder inOrder";
private static final String VERIFY_IN_ORDER_TEMPLATE_PREFIX_2 = " = inOrder(";
private static final String LENIENT_TEMPLATE_PREFIX = "lenient().";

private static final String RETURN_TEMPLATE_PREFIX = "thenReturn(";
private static final String THROW_TEMPLATE_PREFIX = "thenThrow(";
private static final String LITERAL_TEMPLATE_FIELD = "#{}";
private static final String ANY_TEMPLATE_FIELD = "#{any()}";
private static final String MOCKITO_IMPORT_FQN_PREFX = "org.mockito.Mockito";
private static final String IN_ORDER_IMPORT_FQN = "org.mockito.InOrder";

private static String getObjectTemplateField(String fqn) {
return "#{any(" + fqn + ")}";
Expand All @@ -53,6 +56,7 @@ private static String getObjectTemplateField(String fqn) {
private final ExecutionContext ctx;
private final J.NewClass newExpectations;
private final JMockitBlockType blockType;
private final int verificationsInOrderIdx;
// index of the Expectations block in the method body
private final int bodyStatementIndex;
private J.Block methodBody;
Expand All @@ -69,13 +73,14 @@ boolean isRewriteFailed() {
private int numStatementsAdded = 0;

JMockitBlockRewriter(JavaVisitor<ExecutionContext> visitor, ExecutionContext ctx, J.Block methodBody,
J.NewClass newExpectations, int bodyStatementIndex, JMockitBlockType blockType) {
J.NewClass newExpectations, int bodyStatementIndex, JMockitBlockType blockType, int verificationsInOrderIdx) {
this.visitor = visitor;
this.ctx = ctx;
this.methodBody = methodBody;
this.newExpectations = newExpectations;
this.bodyStatementIndex = bodyStatementIndex;
this.blockType = blockType;
this.verificationsInOrderIdx = verificationsInOrderIdx;
this.nextStatementCoordinates = newExpectations.getCoordinates().replace();
}

Expand Down Expand Up @@ -109,9 +114,8 @@ J.Block rewriteMethodBody() {
methodInvocationIdx++;
methodInvocationsToRewrite.add(new ArrayList<>());
}
if (isFullVerifications() &&
uniqueMocks.stream().noneMatch(mock -> mock.getType().equals(mockObj.getType()) &&
mock.getSimpleName().equals(mockObj.getSimpleName()))) {
if ((isFullVerifications() || isVerificationsInOrder()) &&
uniqueMocks.stream().noneMatch(mock -> mock.getSimpleName().equals(mockObj.getSimpleName()))) {
uniqueMocks.add(mockObj);
}
}
Expand All @@ -128,11 +132,16 @@ J.Block rewriteMethodBody() {
removeBlock();
}

List<Object> mocks = new ArrayList<>(uniqueMocks);
if (isVerificationsInOrder()) {
rewriteInOrderVerify(mocks);
}

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

if (isFullVerifications()) {
rewriteFullVerify(new ArrayList<>(uniqueMocks));
rewriteFullVerify(mocks);
}
return methodBody;
}
Expand All @@ -141,6 +150,10 @@ private boolean isFullVerifications() {
return this.blockType == FullVerifications;
}

private boolean isVerificationsInOrder() {
return this.blockType == VerificationsInOrder;
}

private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
final MockInvocationResults mockInvocationResults = buildMockInvocationResults(statementsToRewrite);
if (mockInvocationResults == null) {
Expand Down Expand Up @@ -223,7 +236,7 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
// for Verifications, replace the Verifications block
verifyCoordinates = nextStatementCoordinates;
} else {
// for Expectations put the verify at the end of the method
// for Expectations put verify at the end of the method
verifyCoordinates = methodBody.getCoordinates().lastStatement();
}
rewriteTemplate(verifyTemplate, templateParams, verifyCoordinates);
Expand All @@ -244,19 +257,38 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
}

private void rewriteFullVerify(List<Object> mocks) {
if (!mocks.isEmpty()) {
StringBuilder sb = new StringBuilder(VERIFY_NO_INTERACTIONS_TEMPLATE_PREFIX);
mocks.forEach(mock -> 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);
}
if (rewriteMultipleMocks(mocks, VERIFY_NO_INTERACTIONS_TEMPLATE_PREFIX)) { // verifyNoMoreInteractions(mock1, mock2 ...
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "verifyNoMoreInteractions", false);
}
}

private void rewriteInOrderVerify(List<Object> mocks) {
StringBuilder sb = new StringBuilder(VERIFY_IN_ORDER_TEMPLATE_PREFIX_1); // InOrder inOrder
if (verificationsInOrderIdx > 0) {
sb.append(verificationsInOrderIdx); // InOrder inOrder1
}
sb.append(VERIFY_IN_ORDER_TEMPLATE_PREFIX_2); // InOrder inOrder1 = inOrder(
if (rewriteMultipleMocks(mocks, sb.toString())) { // InOrder inOrder = inOrder(mock1, mock2 ..)
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "inOrder", false);
visitor.maybeAddImport(IN_ORDER_IMPORT_FQN);
}
}

private boolean rewriteMultipleMocks(List<Object> mocks, String template) {
if (mocks.isEmpty()) {
return false;
}
StringBuilder sb = new StringBuilder(template);
mocks.forEach(mock -> sb.append(ANY_TEMPLATE_FIELD).append(", "));
sb.delete(sb.length() - 2, sb.length());
sb.append(");");
rewriteTemplate(sb.toString(), mocks, nextStatementCoordinates);
if (!this.rewriteFailed) {
setNextStatementCoordinates(++numStatementsAdded);
}
return !this.rewriteFailed;
}

private void setNextStatementCoordinates(int numStatementsAdded) {
if (numStatementsAdded <= 0 && bodyStatementIndex == 0) {
nextStatementCoordinates = methodBody.getCoordinates().firstStatement();
Expand All @@ -278,14 +310,11 @@ private void rewriteTemplate(String template, List<Object> templateParams, JavaC
rewriteCoords) {
int numStatementsBefore = methodBody.getStatements().size();
methodBody = JavaTemplate.builder(template)
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
.staticImports("org.mockito.Mockito.*")
.javaParser(getJavaParser(ctx))
.staticImports(MOCKITO_ALL_IMPORT)
.imports(IN_ORDER_IMPORT_FQN)
.build()
.apply(
new Cursor(visitor.getCursor(), methodBody),
rewriteCoords,
templateParams.toArray()
);
.apply(new Cursor(visitor.getCursor(), methodBody), rewriteCoords, templateParams.toArray());
this.rewriteFailed = methodBody.getStatements().size() <= numStatementsBefore;
}

Expand Down Expand Up @@ -325,7 +354,8 @@ private void rewriteTemplate(String template, List<Object> templateParams, JavaC
return templateBuilder.toString();
}

private static void appendToTemplate(StringBuilder templateBuilder, boolean buildingResults, String templatePrefix,
private static void appendToTemplate(StringBuilder templateBuilder, boolean buildingResults, String
templatePrefix,
String templateField) {
if (!buildingResults) {
templateBuilder.append(templatePrefix);
Expand All @@ -335,8 +365,17 @@ private static void appendToTemplate(StringBuilder templateBuilder, boolean buil
templateBuilder.append(templateField);
}

private static String getVerifyTemplate(List<Expression> arguments, String verificationMode, List<Object> templateParams) {
StringBuilder templateBuilder = new StringBuilder(VERIFY_TEMPLATE_PREFIX); // eg verify(object
private String getVerifyTemplate(List<Expression> arguments, String
verificationMode, List<Object> templateParams) {
StringBuilder templateBuilder = new StringBuilder();
if (isVerificationsInOrder()) {
templateBuilder.append("inOrder");
if (this.verificationsInOrderIdx > 0) {
templateBuilder.append(this.verificationsInOrderIdx);
}
templateBuilder.append(".");
}
templateBuilder.append(VERIFY_TEMPLATE_PREFIX); // eg verify(object
if (!verificationMode.isEmpty()) {
templateBuilder.append(", ").append(verificationMode).append("(#{any(int)})"); // eg verify(object, times(2)
}
Expand Down Expand Up @@ -367,7 +406,8 @@ private static String getVerifyTemplate(List<Expression> arguments, String verif
return templateBuilder.toString();
}

private static @Nullable MockInvocationResults buildMockInvocationResults(List<Statement> expectationStatements) {
private static @Nullable MockInvocationResults buildMockInvocationResults
(List<Statement> expectationStatements) {
final MockInvocationResults resultWrapper = new MockInvocationResults();
for (int i = 1; i < expectationStatements.size(); i++) {
Statement expectationStatement = expectationStatements.get(i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,25 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDecl
J.Block methodBody = ssr.rewriteMethodBody();
List<Statement> statements = methodBody.getStatements();

int verificationsInOrderIdx = 0;
int bodyStatementIndex = 0;
// iterate over each statement in the method body, find Expectations blocks and rewrite them
// iterate over each statement in the method body, find JMockit blocks and rewrite them
while (bodyStatementIndex < statements.size()) {
Statement s = statements.get(bodyStatementIndex);
Optional<JMockitBlockType> blockType = JMockitUtils.getJMockitBlock(s);
if (blockType.isPresent()) {
Optional<JMockitBlockType> blockTypeOpt = JMockitUtils.getJMockitBlock(s);
if (blockTypeOpt.isPresent()) {
JMockitBlockType blockType = blockTypeOpt.get();
JMockitBlockRewriter blockRewriter = new JMockitBlockRewriter(this, ctx, methodBody,
((J.NewClass) s), bodyStatementIndex, blockType.get());
((J.NewClass) s), bodyStatementIndex, blockType, verificationsInOrderIdx);
methodBody = blockRewriter.rewriteMethodBody();
statements = methodBody.getStatements();
// if the expectations rewrite failed, skip the next statement
// if the block rewrite failed, skip the next statement
if (blockRewriter.isRewriteFailed()) {
bodyStatementIndex++;
} else {
if (blockType == JMockitBlockType.VerificationsInOrder) {
verificationsInOrderIdx++;
}
}
} else {
bodyStatementIndex++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ enum JMockitBlockType {
Expectations,
NonStrictExpectations,
Verifications,
VerificationsInOrder,
FullVerifications;

private final String fqn = "mockit." + this.name();

boolean isVerifications() {
return this == Verifications || this == FullVerifications;
return this == Verifications || this == FullVerifications || this == VerificationsInOrder;
}

static String getSupportedTypesStr() {
Expand Down
Loading
Loading