Skip to content

Commit

Permalink
Jmockit mockito full verifications and Refactor tests (#617)
Browse files Browse the repository at this point in the history
* Migrate JMockit FullVerifications to Mockito

* Clean up full verifications migration

* Cleanup up jmockit to mockito migration further

* Refactor jmockit to mockito migration

* Refactor jmockit to mockito migration for code reuse

* Cleanup jmockit to mockito base test class

* Refactor jmockit to mockito

* Refactor jmockit to mockito full verifications

* Add license to test base class
  • Loading branch information
shivanisky authored Oct 22, 2024
1 parent 49b3eb5 commit 8a6982b
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(";
Expand Down Expand Up @@ -95,17 +96,27 @@ 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> uniqueMocks = 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<>());
Expression select = invocation.getSelect();
if (select instanceof J.Identifier) {
J.Identifier mockObj = (J.Identifier) select;
// 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 (isFullVerifications() && uniqueMocks.stream().noneMatch(mock -> mock.getType().equals(mockObj.getType())
&& mock.getSimpleName().equals(mockObj.getSimpleName()))) {
uniqueMocks.add(mockObj);
}
}
}

// add the statements corresponding to the method invocation
if (methodInvocationIdx != -1) {
methodInvocationsToRewrite.get(methodInvocationIdx).add(jmockitBlockStatement);
}
Expand All @@ -118,9 +129,17 @@ J.Block rewriteMethodBody() {

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

if (isFullVerifications()) {
rewriteFullVerify(new ArrayList<>(uniqueMocks));
}
return methodBody;
}

private boolean isFullVerifications() {
return this.blockType == FullVerifications;
}

private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
final MockInvocationResults mockInvocationResults = buildMockInvocationResults(statementsToRewrite);
if (mockInvocationResults == null) {
Expand All @@ -136,7 +155,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;
}
Expand Down Expand Up @@ -171,7 +190,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;
}
Expand Down Expand Up @@ -199,19 +218,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
}

Expand All @@ -223,6 +242,20 @@ 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);
}
}
}

private void setNextStatementCoordinates(int numStatementsAdded) {
if (numStatementsAdded <= 0 && bodyStatementIndex == 0) {
nextStatementCoordinates = methodBody.getCoordinates().firstStatement();
Expand All @@ -240,7 +273,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)
Expand All @@ -252,7 +285,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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,25 @@

import lombok.Getter;

import java.util.Arrays;

@Getter
enum JMockitBlockType {

Expectations,
NonStrictExpectations,
Verifications,
NonStrictExpectations;
FullVerifications;

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

private final String fqn;
boolean isVerifications() {
return this == Verifications || this == FullVerifications;
}

JMockitBlockType() {
this.fqn = "mockit." + this.name();
static String getSupportedTypesStr() {
StringBuilder sb = new StringBuilder();
Arrays.stream(values()).forEach(value -> sb.append(value).append(", "));
return sb.substring(0, sb.length() - 2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,10 @@

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class JMockitAnnotatedArgumentToMockitoTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec
.parser(JavaParser.fromJavaVersion()
.logCompilationWarningsAndErrors(true)
.classpathFromResources(new InMemoryExecutionContext(),
"junit-jupiter-api-5.9",
"jmockit-1.49"
))
.recipeFromResource(
"/META-INF/rewrite/jmockit.yml",
"org.openrewrite.java.testing.jmockit.JMockitToMockito"
);
}
class JMockitAnnotatedArgumentToMockitoTest extends JMockitTestBase {

@DocumentExample
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;
import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.setDefaultParserSettings;

/**
* At the moment, JMockit Delegates are not migrated to mockito. What I'm seeing is that they are being trashed
Expand All @@ -32,12 +29,7 @@
*/
@Disabled
@Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/522")
class JMockitDelegateToMockitoTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
setDefaultParserSettings(spec);
}
class JMockitDelegateToMockitoTest extends JMockitTestBase {

@DocumentExample
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,10 @@
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;
import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.setDefaultParserSettings;

class JMockitExpectationsToMockitoTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
setDefaultParserSettings(spec);
}
class JMockitExpectationsToMockitoTest extends JMockitTestBase {

@DocumentExample
@Test
Expand Down
Loading

0 comments on commit 8a6982b

Please sign in to comment.