Skip to content

Commit

Permalink
Replace Mockito 1.x any(Class) and anyString() with nullable(Class) (#…
Browse files Browse the repository at this point in the history
…320)

* Replace Mockito 1.x any(Class) and anyString() with nullable(Class)

Fixes #319

* fix error of missing types

* improve performance

* Update src/main/java/org/openrewrite/java/testing/mockito/AnyStringToNullable.java

Co-authored-by: Tim te Beek <[email protected]>

* pass CI

* Verify AnyToNullable

* Require the old mockito-all to be present before changes

* Restore JunitMockitoUpgradeIntegrationTest

* Add UsesMockitoAll to workaround anySource AND usage

As kindly suggested by @kunli2

---------

Co-authored-by: Kun Li <[email protected]>
Co-authored-by: Kun Li <[email protected]>
  • Loading branch information
3 people authored Mar 7, 2023
1 parent 4de66d5 commit 328e0b1
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 1 deletion.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ recipeDependencies {
val rewriteVersion = rewriteRecipe.rewriteVersion.get()
dependencies {
implementation("org.openrewrite:rewrite-java:$rewriteVersion")
implementation("org.openrewrite:rewrite-gradle:$rewriteVersion")
implementation("org.openrewrite:rewrite-maven:$rewriteVersion")
runtimeOnly("org.openrewrite:rewrite-java-17:$rewriteVersion")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.
*/
package org.openrewrite.java.testing.mockito;

import org.openrewrite.*;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.*;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;

import java.time.Duration;

/**
* Replace Mockito 1.x `anyString()` with `nullable(String.class)`
*/
public class AnyStringToNullable extends Recipe {
private static final MethodMatcher ANY_STRING = new MethodMatcher("org.mockito.Mockito anyString()");
private static final String MOCKITO_CLASS_PATH = "mockito-core-3.12.4";
private static J.MethodInvocation nullableStringMethodTemplate = null;

@Override
public String getDisplayName() {
return "Replace Mockito 1.x `anyString()` with `nullable(String.class)`";
}

@Override
public String getDescription() {
return "Since Mockito 2.10 `anyString()` no longer matches null values. Use `nullable(Class)` instead.";
}

@Override
public Duration getEstimatedEffortPerOccurrence() {
return Duration.ofMinutes(1);
}

@Nullable
@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
return new UsesMethod<>(ANY_STRING);
}

@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
J.MethodInvocation mi = super.visitMethodInvocation(method, executionContext);

if (ANY_STRING.matches(mi)) {
maybeAddImport("org.mockito.ArgumentMatchers", "nullable", false);
maybeRemoveImport("org.mockito.Mockito.anyString");
return getNullableMethodTemplate().withPrefix(mi.getPrefix());
}
return mi;
}
};
}

private static J.MethodInvocation getNullableMethodTemplate() {
if (nullableStringMethodTemplate == null) {
nullableStringMethodTemplate = PartProvider.buildPart("import static org.mockito.ArgumentMatchers" +
".nullable;\n" +
"public class A {\n" +
" void method() {\n" +
" Object x = nullable(String.class);\n" +
" }\n" +
"}", J.MethodInvocation.class, MOCKITO_CLASS_PATH);
}
return nullableStringMethodTemplate;
}
}
35 changes: 35 additions & 0 deletions src/main/resources/META-INF/rewrite/mockito.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ recipeList:
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.mockito.MockitoAnnotations.Mock
newFullyQualifiedTypeName: org.mockito.Mock
- org.openrewrite.java.testing.mockito.AnyToNullable
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: org.mockito.Matchers
newFullyQualifiedTypeName: org.mockito.ArgumentMatchers
Expand Down Expand Up @@ -121,6 +122,40 @@ recipeList:
newVersion: 3.x
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.testing.mockito.UsesMockitoAll
displayName: Uses Mockito all from v1.x
description: Finds projects that depend on `mockito-all` through Maven or Gradle.
tags:
- testing
- mockito
recipeList:
- org.openrewrite.maven.search.FindDependency:
groupId: org.mockito
artifactId: mockito-all
- org.openrewrite.gradle.search.FindDependency:
groupId: org.mockito
artifactId: mockito-all
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.testing.mockito.AnyToNullable
displayName: Replace Mockito 1.x `any(Class)` and `anyString()` with `nullable(Class)`
description: Since Mockito 2.10 `any(Class)` and `anyString()` no longer match null values. Use `nullable(Class)` instead.
tags:
- testing
- mockito
applicability:
anySource:
- org.openrewrite.java.testing.mockito.UsesMockitoAll
recipeList:
- org.openrewrite.java.ChangeMethodName:
methodPattern: org.mockito.Mockito any(java.lang.Class)
newMethodName: nullable
- org.openrewrite.java.ChangeMethodTargetToStatic:
methodPattern: org.mockito.Mockito nullable(java.lang.Class)
fullyQualifiedTargetTypeName: org.mockito.ArgumentMatchers
- org.openrewrite.java.testing.mockito.AnyStringToNullable
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.testing.mockito.ReplacePowerMockito
displayName: Replace Powermock with raw Mockito
description: Replace Powermock with raw Mockito.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* 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.
*/
package org.openrewrite.java.testing.mockito;

import org.junit.jupiter.api.Test;
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 AnyStringToNullableTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec
.parser(JavaParser.fromJavaVersion()
.classpathFromResources(new InMemoryExecutionContext(), "mockito-core-3.12.4")
.logCompilationWarningsAndErrors(true))
.recipe(new AnyStringToNullable());
}

@Test
void replaceAnyStringWithNullableStringClass() {
//language=java
rewriteRun(
java("""
class Example {
String greet(String name) {
return "Hello " + name;
}
}
"""),
java(
"""
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class MyTest {
void test() {
Example example = mock(Example.class);
when(example.greet(anyString())).thenReturn("Hello world");
}
}
""",
"""
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class MyTest {
void test() {
Example example = mock(Example.class);
when(example.greet(nullable(String.class))).thenReturn("Hello world");
}
}
"""
)
);
}

@Test
void doNotReplaceAnyInt() {
//language=java
rewriteRun(
java("""
class Example {
String greet(int value) {
return "Hello " + value;
}
}
"""),
java(
"""
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.anyInt;
class MyTest {
void test() {
Example example = mock(Example.class);
when(example.greet(anyInt())).thenReturn("Hello 5");
}
}
"""
)
);
}

}


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

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

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

class AnyToNullableTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec
.parser(JavaParser.fromJavaVersion()
.classpathFromResources(new InMemoryExecutionContext(), "mockito-core-3.12.4")
.logCompilationWarningsAndErrors(true))
.recipe(Environment.builder()
.scanRuntimeClasspath("org.openrewrite.java.testing.mockito")
.build()
.activateRecipes("org.openrewrite.java.testing.mockito.AnyToNullable"));
}

@Test
void replaceAnyClassWithNullableClass() {
//language=java
rewriteRun(
//language=xml
pomXml("""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>foo</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
</dependency>
</dependencies>
</project>
"""),
//language=java
java("""
class Example {
String greet(Object obj) {
return "Hello " + obj;
}
}
"""),
//language=java
java(
"""
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
class MyTest {
void test() {
Example example = mock(Example.class);
when(example.greet(any(Object.class))).thenReturn("Hello world");
}
}
""",
"""
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class MyTest {
void test() {
Example example = mock(Example.class);
when(example.greet(nullable(Object.class))).thenReturn("Hello world");
}
}
"""
)
);
}

}


Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static class Foo {
int bar(byte[] bytes, String[] s, int i) { return 0; }
boolean baz(String ... strings) { return true; }
}
public void usesMatchers() {
Foo mockFoo = mock(Foo.class);
when(mockFoo.bool(anyString(), anyInt(), any(Object.class))).thenReturn(true);
Expand Down

0 comments on commit 328e0b1

Please sign in to comment.