Skip to content

Commit

Permalink
Migrate JMockit Verifications to Mockito (#540)
Browse files Browse the repository at this point in the history
* First draft of adding feature to migration JMockit Verifications to Mockito verify statements. Tests are failing, need to fix and also add tests once they pass.

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Add more test cases, fix existing test cases, refactor method name. Some test still failing.

* Refactor everything to be simpler to understand, also remove empty new line between migrated mockit when statements as mockito statements are more compact than Expectations

* Add support for multiple statements in Verifications to be migrated by replacing the Verification block

* Add test case with blank lines in Verification block to be erased

* Remove trailing whitespaces in text blocks

* Refactor

* Refactor method and variable names.  One test case failing when we use an external class to mock an object

* Try to debug issue where seeing very strange behaviour in code, where when we change the expected output to the actual generated output by including the static import, we get error that the type is missing or malformed. This looks like a bug in the framework. It seems to only occur when we add a second class in the before.

* Skip type validation after template `verify(#{any()}).#{}(#{any()})`

* Add missing nullable annotations; add Lombok annotations

* Apply suggestions from code review

---------

Co-authored-by: Tim te Beek <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tim te Beek <[email protected]>
  • Loading branch information
4 people authored Jul 3, 2024
1 parent fd0eb2d commit 2096894
Show file tree
Hide file tree
Showing 9 changed files with 1,175 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class ArgumentMatchersRewriter {
}

private static final Map<JavaType.Primitive, String> PRIMITIVE_TO_MOCKITO_ARGUMENT_MATCHER = new HashMap<>();

static {
PRIMITIVE_TO_MOCKITO_ARGUMENT_MATCHER.put(JavaType.Primitive.Int, "anyInt");
PRIMITIVE_TO_MOCKITO_ARGUMENT_MATCHER.put(JavaType.Primitive.Long, "anyLong");
Expand All @@ -81,7 +82,7 @@ class ArgumentMatchersRewriter {
this.expectationsBlock = expectationsBlock;
}

J.Block rewriteExpectationsBlock() {
J.Block rewriteJMockitBlock() {
List<Statement> newStatements = new ArrayList<>(expectationsBlock.getStatements().size());
for (Statement expectationStatement : expectationsBlock.getStatements()) {
// for each statement, check if it's a method invocation and replace any argument matchers
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,31 @@
import org.openrewrite.java.tree.Statement;

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

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

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

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

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesType<>("mockit.Expectations", false),
new RewriteExpectationsVisitor());
return Preconditions.check(Preconditions.or(
new UsesType<>(JMockitBlockType.Expectations.getFqn(), false),
new UsesType<>(JMockitBlockType.Verifications.getFqn(), false)
), new RewriteJMockitBlockVisitor());
}

private static class RewriteExpectationsVisitor extends JavaIsoVisitor<ExecutionContext> {
private static class RewriteJMockitBlockVisitor extends JavaIsoVisitor<ExecutionContext> {

@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) {
Expand All @@ -63,17 +67,19 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDecl
int bodyStatementIndex = 0;
// iterate over each statement in the method body, find Expectations blocks and rewrite them
while (bodyStatementIndex < statements.size()) {
if (!JMockitUtils.isValidExpectationsNewClassStatement(statements.get(bodyStatementIndex))) {
bodyStatementIndex += 1;
continue;
}
ExpectationsBlockRewriter ebr = new ExpectationsBlockRewriter(this, ctx, methodBody,
((J.NewClass) statements.get(bodyStatementIndex)), bodyStatementIndex);
methodBody = ebr.rewriteMethodBody();
statements = methodBody.getStatements();
// if the expectations rewrite failed, skip the next statement
if (ebr.isExpectationsRewriteFailed()) {
bodyStatementIndex += 1;
Statement s = statements.get(bodyStatementIndex);
Optional<JMockitBlockType> blockType = JMockitUtils.getJMockitBlock(s);
if (blockType.isPresent()) {
JMockitBlockRewriter blockRewriter = new JMockitBlockRewriter(this, ctx, methodBody,
((J.NewClass) s), bodyStatementIndex, blockType.get());
methodBody = blockRewriter.rewriteMethodBody();
statements = methodBody.getStatements();
// if the expectations rewrite failed, skip the next statement
if (blockRewriter.isRewriteFailed()) {
bodyStatementIndex++;
}
} else {
bodyStatementIndex++;
}
}
return md.withBody(methodBody);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.testing.jmockit;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
enum JMockitBlockType {

Expectations("mockit.Expectations"),
Verifications("mockit.Verifications"); // Add NonStrictExpectations later

private final String fqn;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,32 @@
import org.openrewrite.java.tree.Statement;
import org.openrewrite.java.tree.TypeUtils;

import java.util.Optional;

import static java.util.Optional.empty;

class JMockitUtils {
static boolean isValidExpectationsNewClassStatement(Statement s) {

static Optional<JMockitBlockType> getJMockitBlock(Statement s) {
if (!(s instanceof J.NewClass)) {
return false;
return empty();
}
J.NewClass nc = (J.NewClass) s;
if (!(nc.getClazz() instanceof J.Identifier)) {
return false;
return empty();
}
J.Identifier clazz = (J.Identifier) nc.getClazz();
if (!TypeUtils.isAssignableTo("mockit.Expectations", clazz.getType())) {
return false;

// JMockit block should be composed of a block within another block
if (nc.getBody() == null || nc.getBody().getStatements().size() != 1) {
return empty();
}

for (JMockitBlockType blockType : JMockitBlockType.values()) {
if (TypeUtils.isAssignableTo(blockType.getFqn(), clazz.getType())) {
return Optional.of(blockType);
}
}
// Expectations block should be composed of a block within another block
return nc.getBody() != null && nc.getBody().getStatements().size() == 1;
return empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ class SetupStatementsRewriter {

J.Block rewriteMethodBody() {
List<Statement> statements = methodBody.getStatements();
// 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
for (Statement s : statements) {
if (!JMockitUtils.isValidExpectationsNewClassStatement(s)) {
if (!JMockitUtils.getJMockitBlock(s).isPresent()) {
continue;
}
J.NewClass nc = (J.NewClass) s;
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/META-INF/rewrite/jmockit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ tags:
- testing
- jmockit
recipeList:
- org.openrewrite.java.testing.jmockit.JMockitExpectationsToMockito
- org.openrewrite.java.testing.jmockit.JMockitBlockToMockito
- org.openrewrite.java.testing.jmockit.JMockitAnnotatedArgumentToMockito
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: mockit.Mocked
Expand Down
Loading

0 comments on commit 2096894

Please sign in to comment.