Compare commits

...

4 Commits

Author SHA1 Message Date
Stephan Schroevers
d039af8002 Simpler 2021-02-27 12:51:08 +01:00
Stephan Schroevers
8fdf3635fd Fix the build 2021-02-27 12:51:08 +01:00
Arturs Drozdovs
54f93ffa8c Cleanup / add proper fix suggestions 2021-02-27 12:51:08 +01:00
Arturs Drozdovs
6b4fe2eabd WIP Mockito strict stubs enforcement 2021-02-27 12:51:08 +01:00
4 changed files with 156 additions and 0 deletions

View File

@@ -147,6 +147,11 @@
<artifactId>mockito-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>

View File

@@ -0,0 +1,75 @@
package tech.picnic.errorprone.bugpatterns;
import static com.google.errorprone.matchers.ChildMultiMatcher.MatchType.AT_LEAST_ONE;
import static com.google.errorprone.matchers.Description.NO_MATCH;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.annotations;
import static com.google.errorprone.matchers.Matchers.hasArgumentWithValue;
import static com.google.errorprone.matchers.Matchers.isSameType;
import static com.google.errorprone.matchers.Matchers.isType;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.BugPattern.LinkType;
import com.google.errorprone.BugPattern.ProvidesFix;
import com.google.errorprone.BugPattern.SeverityLevel;
import com.google.errorprone.BugPattern.StandardTags;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.Tree;
/** A {@link BugChecker} which flags classes importing Mockito, but not enforcing strict mocks. */
@AutoService(BugChecker.class)
@BugPattern(
name = "MockitoAnnotation",
summary = "Prefer using strict stubs with Mockito",
linkType = LinkType.NONE,
severity = SeverityLevel.SUGGESTION,
tags = StandardTags.STYLE,
providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION)
public final class MockitoAnnotationCheck extends BugChecker implements ClassTreeMatcher {
private static final long serialVersionUID = 1L;
private static final String MOCKITO_SETTINGS = "org.mockito.junit.jupiter.MockitoSettings";
private static final String STRICT_STUBS = "org.mockito.quality.Strictness.STRICT_STUBS";
private static final String MOCKITO_ANNOTATION = "@MockitoSettings(strictness = STRICT_STUBS)";
private static final MultiMatcher<Tree, AnnotationTree> HAS_STRICT_STUBS_ANNOTATION =
annotations(
AT_LEAST_ONE,
allOf(
isType(MOCKITO_SETTINGS),
hasArgumentWithValue("strictness", isSameType(STRICT_STUBS))));
@Override
public Description matchClass(ClassTree clazz, VisitorState state) {
if (ASTHelpers.findEnclosingNode(state.getPath(), ClassTree.class) != null
|| HAS_STRICT_STUBS_ANNOTATION.matches(clazz, state)
|| !importsMockito(state)) {
return NO_MATCH;
}
return describeMatch(clazz, buildFix(clazz));
}
private static boolean importsMockito(VisitorState state) {
return state.getPath().getCompilationUnit().getImports().stream()
.map(ImportTree::getQualifiedIdentifier)
.map(Object::toString)
.anyMatch(importLine -> importLine.startsWith("org.mockito"));
}
private static SuggestedFix buildFix(ClassTree clazz) {
return SuggestedFix.builder()
.addImport(MOCKITO_SETTINGS)
.addStaticImport(STRICT_STUBS)
.prefixWith(clazz, MOCKITO_ANNOTATION)
.build();
}
}

View File

@@ -0,0 +1,71 @@
package tech.picnic.errorprone.bugpatterns;
import com.google.errorprone.BugCheckerRefactoringTestHelper;
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
import com.google.errorprone.CompilationTestHelper;
import org.junit.jupiter.api.Test;
public final class MockitoAnnotationCheckTest {
private final CompilationTestHelper compilationTestHelper =
CompilationTestHelper.newInstance(MockitoAnnotationCheck.class, getClass());
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
BugCheckerRefactoringTestHelper.newInstance(new MockitoAnnotationCheck(), getClass());
@Test
public void testIdentification() {
compilationTestHelper
.addSourceLines(
"A.java",
"import static org.mockito.Mockito.mock;",
"",
"import org.junit.jupiter.api.Tag;",
"import org.junit.jupiter.api.Test;",
"",
"@Tag(\"unit\")",
"// BUG: Diagnostic contains:",
"class MockitoTest {",
" @Test",
" void mockitoTest() {",
" mock(String.class);",
" }",
"}")
.doTest();
}
@Test
public void testReplacement() {
refactoringTestHelper
.addInputLines(
"in/A.java",
"import static org.mockito.Mockito.mock;",
"",
"import org.junit.jupiter.api.Tag;",
"import org.junit.jupiter.api.Test;",
"",
"@Tag(\"unit\")",
"class MockitoTest {",
" @Test",
" void mockitoTest() {",
" mock(String.class);",
" }",
"}")
.addOutputLines(
"out/A.java",
"import static org.mockito.Mockito.mock;",
"import static org.mockito.quality.Strictness.STRICT_STUBS;",
"",
"import org.junit.jupiter.api.Tag;",
"import org.junit.jupiter.api.Test;",
"import org.mockito.junit.jupiter.MockitoSettings;",
"",
"@MockitoSettings(strictness = STRICT_STUBS)",
"@Tag(\"unit\")",
"class MockitoTest {",
" @Test",
" void mockitoTest() {",
" mock(String.class);",
" }",
"}")
.doTest(TestMode.TEXT_MATCH);
}
}

View File

@@ -296,6 +296,11 @@
<artifactId>mockito-core</artifactId>
<version>${version.mockito}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${version.mockito}</version>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>