Skip to content

Commit

Permalink
Retain Mockito Strictness WARN when switching to JUnit 5 through `@Mo…
Browse files Browse the repository at this point in the history
…ckitoSettings(strictness = Strictness.WARN)` (#584)

* WIP first setup

* Apply suggestions from code review

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

* WIP test is failing

* Introduce scanning recipe to check for dependency

As preconditions only match the exact same file, and there's no guaranteed dependency marker available when visiting compilation units.

* Provide more context as to why this recipe exists in the description
Correct header year
Add comment about limitation of the recipe

* Update recipe name, description & target strictness

* Update recipe name once again

* Also pick up mockito-all

---------

Co-authored-by: Laurens Westerlaken <laurens.westerlaken@jdriven.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Tim te Beek <tim@moderne.io>
  • Loading branch information
4 people authored Aug 21, 2024
1 parent 52502ed commit 108e7fd
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.mockito;

import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.RemoveAnnotation;
import org.openrewrite.java.dependencies.DependencyInsight;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;

import java.util.Comparator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

public class RetainStrictnessWarn extends ScanningRecipe<AtomicBoolean> {

private static final String EXTEND_WITH_FQ = "org.junit.jupiter.api.extension.ExtendWith";
private static final String MOCKITO_EXTENSION_FQ = "org.mockito.junit.jupiter.MockitoExtension";
private static final String MOCKITO_SETTINGS_FQ = "org.mockito.junit.jupiter.MockitoSettings";
private static final String MOCKITO_STRICTNESS_FQ = "org.mockito.quality.Strictness";

private static final String EXTEND_WITH_MOCKITO_EXTENSION = "@" + EXTEND_WITH_FQ + "(" + MOCKITO_EXTENSION_FQ + ".class)";

@Override
public String getDisplayName() {
return "Retain Mockito strictness `WARN` when switching to JUnit 5";
}

@Override
public String getDescription() {
return "Migrating from JUnit 4 to 5 [changes the default strictness](https://stackoverflow.com/a/53234137/53444) of the mocks from `WARN` to `STRICT_STUBS`. " +
"To prevent tests from failing we restore the original behavior by adding `@MockitoSettings(strictness = Strictness.WARN)`.";
}

@Override
public AtomicBoolean getInitialValue(ExecutionContext ctx) {
return new AtomicBoolean(false);
}

@Override
public TreeVisitor<?, ExecutionContext> getScanner(AtomicBoolean usingOlderMockito) {
TreeVisitor<?, ExecutionContext> div = new DependencyInsight("org.mockito", "mockito-*", "[1.1,2.17)").getVisitor();
return new TreeVisitor<Tree, ExecutionContext>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
if (!usingOlderMockito.get() && div.visit(tree, ctx) != tree) {
usingOlderMockito.set(true);
}
return tree;
}
};
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor(AtomicBoolean usingOlderMockito) {
return Preconditions.check(usingOlderMockito.get(),
Preconditions.check(
Preconditions.and(
new UsesType<>(MOCKITO_EXTENSION_FQ, true),
Preconditions.not(new UsesType<>(MOCKITO_SETTINGS_FQ, false))
), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
Set<J.Annotation> annotations = FindAnnotations.find(classDecl, EXTEND_WITH_MOCKITO_EXTENSION);
if (!annotations.isEmpty()) {
maybeAddImport(MOCKITO_SETTINGS_FQ);
maybeAddImport(MOCKITO_STRICTNESS_FQ);
classDecl = JavaTemplate.builder("@MockitoSettings(strictness = Strictness.WARN)")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-junit-jupiter", "mockito-core"))
.imports(MOCKITO_SETTINGS_FQ, MOCKITO_STRICTNESS_FQ)
.build()
.apply(getCursor(), classDecl.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
doAfterVisit(new RemoveAnnotation(EXTEND_WITH_MOCKITO_EXTENSION).getVisitor());
maybeRemoveImport(EXTEND_WITH_FQ);
maybeRemoveImport(MOCKITO_EXTENSION_FQ);
}
return super.visitClassDeclaration(classDecl, ctx);
}
})
);
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/rewrite/mockito.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ recipeList:
- org.openrewrite.java.testing.junit5.MockitoJUnitToMockitoExtension
# https://github.com/openrewrite/rewrite-testing-frameworks/issues/360
# - org.openrewrite.java.testing.mockito.ReplacePowerMockito
- org.openrewrite.java.testing.mockito.RetainStrictnessWarn
- org.openrewrite.java.dependencies.AddDependency:
groupId: org.mockito
artifactId: mockito-junit-jupiter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* 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.mockito;

import org.intellij.lang.annotations.Language;
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;
import static org.openrewrite.maven.Assertions.pomXml;

class RetainStrictnessWarnTest implements RewriteTest {

@Language("xml")
private static final String POM_XML_WITH_OLDER_MOCKITO = """
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>bla.bla</groupId>
<artifactId>bla-bla</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
""";

@Language("java")
private static final String JAVA_BEFORE = """
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class MyTest {
}
""";

@Language("java")
private static final String JAVA_AFTER = """
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@MockitoSettings(strictness = Strictness.WARN)
class MyTest {
}
""";

@Override
public void defaults(RecipeSpec spec) {
spec
.parser(JavaParser.fromJavaVersion()
.classpathFromResources(new InMemoryExecutionContext(),
"mockito-core", "mockito-junit-jupiter", "junit-jupiter-api"))
.recipe(new RetainStrictnessWarn());
}

@Test
@DocumentExample
void shouldAddMockitoSettingsWithLenientStubbing() {
//language=java
rewriteRun(
pomXml(POM_XML_WITH_OLDER_MOCKITO),
java(JAVA_BEFORE, JAVA_AFTER)
);
}

@Test
void shouldLeaveExisting() {
//language=java
rewriteRun(
pomXml(POM_XML_WITH_OLDER_MOCKITO),
java(
"""
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@MockitoSettings(strictness = Strictness.STRICT_STUBS)
class MyTest {
}
"""
)
);
}

@Test
void shouldRunBeforeMockitoCore2_17() {
rewriteRun(
pomXml(POM_XML_WITH_OLDER_MOCKITO),
java(JAVA_BEFORE, JAVA_AFTER)
);
}

@Test
void shouldNotRunOnNewerMockito() {
rewriteRun(
//language=xml
pomXml(
"""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>bla.bla</groupId>
<artifactId>bla-bla</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.17.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
"""
),
java(JAVA_BEFORE)
);
}
}

0 comments on commit 108e7fd

Please sign in to comment.