Skip to content

Commit

Permalink
Rewrite both JMockit @Mocked and @Injectable annotated arguments (#…
Browse files Browse the repository at this point in the history
…532)

* Ensure Jmockit expectations with no times or result transform to a mockito verify

* Minor polish to text blocks

* Make times, minTimes, maxTimes more flexible as it was originally so even if someone mistakenly puts times and minTimes together, it still migrates without issue

* Add feature to enable migration of Jmockit Injectable annotation exactly as we are doing the Mocked annotation ie method parameter annotation as well as field annotation. Have moved all of the code from JMockitMockedVariableToMockito to JMockitAnnotationToMockito for code reuse. Also add the corresponding test cases and jmockit.yml file modification.

* Use `@Option` instead of inheritance

* Drop Option and just replace both variants

* Update display name and description to match application

---------

Co-authored-by: Tim te Beek <tim@moderne.io>
  • Loading branch information
shivanisky and timtebeek committed Jun 20, 2024
1 parent 288c9ef commit 8388b28
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 116 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
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;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Statement;

import java.util.ArrayList;
import java.util.List;

@EqualsAndHashCode(callSuper = false)
public class JMockitAnnotatedArgumentToMockito extends Recipe {
@Override
public String getDisplayName() {
return "Convert JMockit `@Mocked` and `@Injectable` annotated arguments";
}

@Override
public String getDescription() {
return "Convert JMockit `@Mocked` and `@Injectable` annotated arguments into Mockito statements.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
Preconditions.or(
new UsesType<>("mockit.Mocked", false),
new UsesType<>("mockit.Injectable", false)
),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) {
J.MethodDeclaration md = super.visitMethodDeclaration(methodDeclaration, ctx);

List<Statement> parameters = md.getParameters();
if (!parameters.isEmpty() && !(parameters.get(0) instanceof J.Empty)) {
maybeRemoveImport("mockit.Injectable");
maybeRemoveImport("mockit.Mocked");
maybeAddImport("org.mockito.Mockito");

// Create lists to store the mocked parameters and the new type parameters
List<J.VariableDeclarations> mockedParameter = new ArrayList<>();

// Remove any mocked parameters from the method declaration
md = md.withParameters(ListUtils.map(parameters, parameter -> {
if (parameter instanceof J.VariableDeclarations) {
J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) parameter;
// Check if the parameter has the annotation "mockit.Mocked or mockit.Injectable"
if (!FindAnnotations.find(variableDeclarations, "mockit.Injectable").isEmpty() ||
!FindAnnotations.find(variableDeclarations, "mockit.Mocked").isEmpty() ) {
mockedParameter.add(variableDeclarations);
return null;
}
}
return parameter;
}));

// 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"))
.imports("org.mockito.Mockito")
.contextSensitive()
.build();
// Retain argument order by iterating in reverse
for (int i = mockedParameter.size() - 1; i >= 0; i--) {
J.VariableDeclarations variableDeclarations = mockedParameter.get(i);
// Apply the template and update the method declaration
md = addStatementsTemplate.apply(updateCursor(md),
md.getBody().getCoordinates().firstStatement(),
variableDeclarations.getTypeExpression().toString(),
variableDeclarations.getVariables().get(0).getSimpleName(),
variableDeclarations.getTypeExpression().toString());
}
}
}
return md;
}
}
);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2021 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.
*/
@NonNullApi
package org.openrewrite.java.testing.jmockit;

import org.openrewrite.internal.lang.NonNullApi;
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 @@ -23,7 +23,7 @@ tags:
- jmockit
recipeList:
- org.openrewrite.java.testing.jmockit.JMockitExpectationsToMockito
- org.openrewrite.java.testing.jmockit.JMockitMockedVariableToMockito
- org.openrewrite.java.testing.jmockit.JMockitAnnotatedArgumentToMockito
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: mockit.Mocked
newFullyQualifiedTypeName: org.mockito.Mock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,15 @@

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

class JMockitMockedVariableToMockitoTest implements RewriteTest {
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",
"mockito-core-3.12",
"mockito-junit-jupiter-3.12"
"jmockit-1.49"
))
.recipeFromResource(
"/META-INF/rewrite/jmockit.yml",
Expand All @@ -52,7 +50,7 @@ void mockedVariableTest() {
import mockit.Mocked;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Mocked
Object mockedObject;
Expand All @@ -68,7 +66,7 @@ void test(@Mocked Object o, @Mocked Object o2) {
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Mock
Object mockedObject;
Expand All @@ -86,15 +84,15 @@ void test() {
}

@Test
void noVariableTest() {
void mockedNoVariableTest() {
rewriteRun(
//language=java
java(
"""
import mockit.Mocked;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Mocked
Object mockedObject;
Expand All @@ -108,7 +106,86 @@ void test() {
import org.mockito.Mock;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Mock
Object mockedObject;
void test() {
assertNotNull(mockedObject);
}
}
"""
)
);
}

@Test
void injectableVariableTest() {
//language=java
rewriteRun(
java(
"""
import mockit.Injectable;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Injectable
Object mockedObject;
void test(@Injectable Object o, @Injectable Object o2) {
assertNotNull(o);
assertNotNull(o2);
}
}
""",
"""
import org.mockito.Mock;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Mock
Object mockedObject;
void test() {
Object o = Mockito.mock(Object.class);
Object o2 = Mockito.mock(Object.class);
assertNotNull(o);
assertNotNull(o2);
}
}
"""
)
);
}

@Test
void injectableNoVariableTest() {
rewriteRun(
//language=java
java(
"""
import mockit.Injectable;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Injectable
Object mockedObject;
void test() {
assertNotNull(mockedObject);
}
}
""",
"""
import org.mockito.Mock;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class A {
@Mock
Object mockedObject;
Expand Down

0 comments on commit 8388b28

Please sign in to comment.