diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index d6ccc0fd..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -patreon: tamasbalog diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dbb1e93..79661019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ ## [Unreleased] +## [1.8.0] +### Changed +- Various minor code improvements and cleanups. +- Improved the code documentation and inspection descriptions. + ## [1.7.0] ### Changed - New supported IDE version range: 2026.1 and later. @@ -92,7 +97,7 @@ and marked falsely. - [#53](https://github.com/picimako/mockitools/issues/53): Added an inspection to report when the mocked type and the type of the spied instance don't match in a `mock(Type.class, withSettings().spiedInstance(...));`-type mock creation. - [#56](https://github.com/picimako/mockitools/issues/56): Added an inspection to report spying on mock objects, i.e. `Mockito.spy(Mockito.mock(...))` and `Mockito.spy()`. - [#57](https://github.com/picimako/mockitools/issues/57): Added an inspection to report arguments passed into `Mockito.mock()` and `Mockito.spy()` which are designed to determine the mock type based on -the variable's type they are assigned to, and not by the type passed into them. See [Mocking/spying without specifying class](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#54). +the variable's type they are assigned to, and not by the type passed into them. See [Mocking/spying without specifying class](https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#54). - [#58](https://github.com/picimako/mockitools/issues/58): Added an inspection to report `MockSettings`-based mock creations that have convenience methods or simpler variants to create the mock. - [#59](https://github.com/picimako/mockitools/issues/59): Added an intention action to expand simple mock and spy creation calls to use specific `MockSettings`, for example `Mockito.mock(, )` is converted to `Mockito.mock(, Mockito.withSettings().defaultAnswer())`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed2cc7e1..42863ca1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,84 +1,113 @@ # Developer Documentation -## Technology stack +## Technology Stack -This project builds on the [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template) that uses -Gradle as its build system, GitHub Actions for the CI/CD pipeline, and Java 17. +This project builds on the [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template) +Gradle as its build system, GitHub Actions for the CI/CD pipeline, and Java/Kotlin for coding. -## Environment setup +### Libraries + +- Production: Lombok +- Testing: JUnit 5, AssertJ + +## Environment Setup ### JDK -Install Java JDK 17 and configure the project to use that JDK if not automatically configured. +Install Java JDK 21 and configure the project to use that JDK if not automatically configured. ### Setup Mockitools -To start development, just clone this repository to your machine, import the project, let it build, and you should be good to go. +To start development, clone this repository to your machine, import the project, let it build, and you should be good to go. -There are predefined run configurations coming from the platform plugin template you can use to build, test and run the plugin. +There are predefined run configurations coming from the platform plugin template you can use to build, test and run the plugin: -### Suggested plugins for development +| Run Configuration | CLI command | Purpose | +|-------------------------|--------------------------|------------------------------------------------------------------| +| **Run Build** | `./gradlew buildPlugin` | Compiles the project and builds the plugin .jar and .zip archive | +| **Run IDE with Plugin** | `./gradlew runIde` | Runs a sandbox IDE with the Mockitools plugin installed in it | +| **Run Tests** | `./gradlew check` | Runs all tests in the project | +| **Run Verifications** | `./gradlew verifyPlugin` | Runs the Plugin Verifier | -the following plugins are recommended to help with the plugin development: +### Suggested Plugins for Development + +The following plugins can help with the plugin development: - [PsiViewer](https://plugins.jetbrains.com/plugin/227-psiviewer) for inspecting the syntax tree (PSI) of files, -- [Concise AssertJ Optimizing Nitpicker (Cajon)](https://plugins.jetbrains.com/plugin/12195-concise-assertj-optimizing-nitpicker-cajon-) for working with AssertJ assertions. +- [Concise AssertJ Optimizing Nitpicker (Cajon)](https://plugins.jetbrains.com/plugin/12195-concise-assertj-optimizing-nitpicker-cajon-) for working with AssertJ assertions in tests. +- [Just Kitting](https://plugins.jetbrains.com/plugin/21139-just-kitting) for extra utilities for plugin development, +and for easier comparison of configuration files with their versions in the upstream template. + +## Project Structure -## Base classes +``` +/ + └── .github/ + └── workflows/ //GitHub Actions workflow files + ├── .run/ //IntelliJ Run Configurations + ├── docs/ //Project documentation + ├── gradle/ //Gradle wrapper files + └── libs.versions.toml //Gradle version catalog + └── src/ + ├── ... + └── test/ + ├── ... + └── testData/ //Test data files for inspections, intentions, etc. +``` -There are a few base classes that can be used to implement the different plugin features: -- [MockitoolsBaseInspection](src/main/java/com/picimako/mockitools/inspection/MockitoolsBaseInspection.java) for inspections (not all inspections use this as base class) -- [MigrationAidBase](src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidBase.java) for migration aids inspections +### Mockito DSL -## Mockito DSL types +Functionality is organized into and around DSL specific classes in the `com.picimako.mockitools.dsl` package. -Functionality is organized into DSL specific classes: -- classes in the `com.picimako.mockitools.dsl` package -- [StubbingApproach](src/main/java/com/picimako/mockitools/StubbingApproach.java) -- [VerificationApproach](src/main/java/com/picimako/mockitools/VerificationApproach.java) -- [StubType](src/main/java/com/picimako/mockitools/StubType.java) +### Base Classes -## Message bundle +There are a few common base classes that can be used to implement the different plugin features. They are all named +consistently with their names ending with `Base`, e.g. `MockitoolsInspectionBase` or `QuickFixBase`. -Most messages that appear on the UI, in settings, in inspection messages, are stored in a message bundle called `MockitoolsBundle.properties`. +### Message Bundle + +Most messages that appear on the UI, in settings, in inspection messages, etc. are stored in a message bundle called `MockitoolsBundle.properties`. Some messages, like inspection titles are not yet included, and simply are in the plugin.xml. -## Since-version +### Integration Tests -The following markers are placed on extension point implementations and more important types, to help identify certain aspects of the code: -- `@since ` in javadocs +Integration tests build on JUnit 5-based platform test classes. There are a couple of base test classes that are +consistently named as `*TestBase`, e.g. `MockitoolsTestBase`. -## CI/CD +#### JDK -For CI/CD, GitHub Actions is integrated, and workflows are available in the `.github/workflows` folder. +Tests and test bases that are dedicated to testing primarily Java test data files require either a mock JDK from +the IntelliJ Platform or a real JDK to be available. -## Functional tests +This project is configured to always use a real JDK due to former issues and test failures when using a mock JDK. -Functional tests build mostly on JUnit3-based platform test classes. For assertions, either the IntelliJ platform's underlying logic is used, or AssertJ -where applicable. +#### Load 3rd-party Libraries -Mockitools base test classes: -- [MockitoolsTestBase](src/test/java/com/picimako/mockitools/MockitoolsTestBase.java) as the common base test class -- [MockitoolsInspectionTestBase](src/test/java/com/picimako/mockitools/inspection/MockitoolsInspectionTestBase.java) for inspections -- [MockitoolsIntentionTestBase](src/test/java/com/picimako/mockitools/intention/MockitoolsIntentionTestBase.java) for intention actions -- [MockitoolsCodeCompletionTestBase](src/test/java/com/picimako/mockitools/completion/MockitoolsCodeCompletionTestBase.java) for completion contributors +Since the test data files are Java files, certain libraries have to be added to the tests' classpath. -Other resources: [IntelliJ Platform Plugin SDK - Testing Overview](https://plugins.jetbrains.com/docs/intellij/testing-plugins.html) +The necessary libraries are collected in the `ThirdPartyLibrary` enum with their Maven coordinates, +and can be configured in tests via the constructor of `MockitoolsTestBase`. -### Load 3rd-party libs +#### Test Data -#### JDK +Test data files are stored in the `src/test/testData` folder. +They can be configured as project roots via the `@TestDataPath` annotation on test classes. + +## CI/CD + +For CI/CD, GitHub Actions is integrated, and workflows are available in `.github/workflows/build.yml`. + +## Documentation + +### General -Java file based tests require either a mock or a real JDK to be available. This project is configured to always use a real JDK, -so that modification of the -[`idea.home.path` system property](https://plugins.jetbrains.com/docs/intellij/code-inspections.html#inspection-unit-test) -is not necessary for running tests locally. And, using the JAVA_HOME based JDK also works on GitHub Actions. +General documentation is stored in the `/docs` root-level folder. -### testData +### Since-version -The `src/test/testData` folder is used to store test data. They can be configured as project roots when setting the test data path in functional tests. +The `@since ` Javadoc tag is placed on extension point implementations and more important types and methods +to help see which version introduced the feature. ## JetBrains resources - [IntelliJ Platform Plugin SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html) - [IntelliJ Platform Plugin Template](https://github.com/JetBrains/intellij-platform-plugin-template) -- [JetBrains Community Support](https://intellij-support.jetbrains.com/hc/en-us/community/topics) -- [Request invitation for JetBrains Slack](https://plugins.jetbrains.com/slack) +- [JetBrains Platform - community forum](https://platform.jetbrains.com) diff --git a/build.gradle.kts b/build.gradle.kts index 4827e779..c621a886 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,57 +3,50 @@ import org.jetbrains.changelog.markdownToHTML import org.jetbrains.intellij.platform.gradle.TestFrameworkType plugins { - id("java") // Java support - alias(libs.plugins.kotlin) // Kotlin support - alias(libs.plugins.intelliJPlatform) // IntelliJ Platform Gradle Plugin - alias(libs.plugins.changelog) // Gradle Changelog Plugin - alias(libs.plugins.lombok) // Lombok + id("java") + alias(libs.plugins.kotlin) + alias(libs.plugins.intelliJPlatform) + alias(libs.plugins.changelog) + alias(libs.plugins.lombok) } group = providers.gradleProperty("pluginGroup").get() version = providers.gradleProperty("pluginVersion").get() // Set the JVM language level used to build the project. -kotlin { - jvmToolchain(21) -} +kotlin { jvmToolchain(21) } // Configure project's dependencies repositories { mavenCentral() - // IntelliJ Platform Gradle Plugin Repositories Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html - intellijPlatform { - defaultRepositories() - } + intellijPlatform { defaultRepositories() } } dependencies { //Testing - //Required for 'junit.framework.TestCase' referenced in 'com.intellij.testFramework.UsefulTestCase' - testImplementation(libs.junit) - testImplementation(libs.assertj) - testImplementation(libs.junitJupiterApi) + setOf( + //Required for 'junit.framework.TestCase' referenced in 'com.intellij.testFramework.UsefulTestCase' + libs.junit, + libs.assertj, + libs.junitJupiterApi, + libs.junitJupiterParams, + libs.junitPlatformLauncher, + ).forEach(::testImplementation) testRuntimeOnly(libs.junitJupiterEngine) - testImplementation(libs.junitJupiterParams) - testImplementation(libs.junitPlatformLauncher) // IntelliJ Platform Gradle Plugin Dependencies Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html intellijPlatform { create(providers.gradleProperty("platformType"), providers.gradleProperty("platformVersion")) - // Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties file for bundled IntelliJ Platform plugins. bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(',') }) - - // Plugin Dependencies. Uses `platformPlugins` property from the gradle.properties file for plugin from JetBrains Marketplace. plugins(providers.gradleProperty("platformPlugins").map { it.split(',') }) pluginVerifier() zipSigner() testFramework(TestFrameworkType.Platform) - //Required for 'LightJavaCodeInsightFixtureTestCase5' testFramework(TestFrameworkType.Plugin.Java) testFramework(TestFrameworkType.JUnit5) } @@ -91,17 +84,10 @@ intellijPlatform { } } - ideaVersion { - sinceBuild = providers.gradleProperty("pluginSinceBuild") -// untilBuild = providers.gradleProperty("pluginUntilBuild") - } + ideaVersion { sinceBuild = providers.gradleProperty("pluginSinceBuild") } } - pluginVerification { - ides { - recommended() - } - } + pluginVerification { ides { recommended() } } } // Configure Gradle Changelog Plugin - read more: https://github.com/JetBrains/gradle-changelog-plugin diff --git a/docs/argument_matching_capture.md b/docs/argument_matching_capture.md index 03f450eb..88b1cffa 100644 --- a/docs/argument_matching_capture.md +++ b/docs/argument_matching_capture.md @@ -27,7 +27,7 @@ The following ways of stubbing are supported by this inspection: - `Mockito.when()`, `BDDMockito.given()`, `Mockito.do...().when()`, matchers in `AdditionalMatchers` You can find more information in the official documents and source code: -- [Argument matchers documentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#argument_matchers) +- [Argument matchers documentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#argument_matchers) - [ArgumentMatchers javadoc](https://javadoc.io/static/org.mockito/mockito-core/3.11.2/org/mockito/ArgumentMatchers.html) - [org.mockito.internal.exceptions.Reporter#invalidUseOfMatchers(int, List)](https://github.com/mockito/mockito/blob/main/src/main/java/org/mockito/internal/exceptions/Reporter.java) - [org.mockito.internal.exceptions.Reporter#incorrectUseOfAdditionalMatchers(String, int, Collection)](https://github.com/mockito/mockito/blob/main/src/main/java/org/mockito/internal/exceptions/Reporter.java) diff --git a/docs/mock_creation.md b/docs/mock_creation.md index ac93f87c..dda2e381 100644 --- a/docs/mock_creation.md +++ b/docs/mock_creation.md @@ -97,7 +97,7 @@ and validates test code regardless of the Mockito version. ![](https://img.shields.io/badge/inspection-orange) ![](https://img.shields.io/badge/since-0.11.0-blue) [![](https://img.shields.io/badge/implementation-GenericInferredMockAndSpyCreationInspection-blue)](../src/main/java/com/picimako/mockitools/inspection/mocking/GenericInferredMockAndSpyCreationInspection.java) -[Mockito 4.9.0](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mock_without_class) introduced an enhancement to `Mockito.mock()` and `Mockito.spy()` +[Mockito 4.9.0](https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#mock_without_class) introduced an enhancement to `Mockito.mock()` and `Mockito.spy()` based mock/spy creation, so that the mock type is not determined by the class passed in, instead by the type of the variable/field it is assigned to. These variants of `mock()` and `spy()` throw an exception if objects other than configuration are passed in, thus this inspection reports @@ -166,7 +166,7 @@ class MockSpiedInstanceTypeMismatch { ![](https://img.shields.io/badge/inspection-orange) ![](https://img.shields.io/badge/since-0.1.0-blue) [![](https://img.shields.io/badge/implementation-CallOnMockitoResetInspection-blue)](../src/main/java/com/picimako/mockitools/inspection/CallOnMockitoResetInspection.java) -Based on Mockito's documentation on [resetting mocks](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#resetting_mocks) +Based on Mockito's documentation on [resetting mocks](https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#resetting_mocks) > Smart Mockito users hardly use this feature because they know it could be a sign of poor tests. Normally, you don't need to reset your mocks, just create new mocks for each test method. diff --git a/docs/verifications.md b/docs/verifications.md index f62fb447..2ea9ae81 100644 --- a/docs/verifications.md +++ b/docs/verifications.md @@ -47,7 +47,7 @@ Mockito.verify(mockObject, timeout(6000)).method(); //over threshold, since 5000 This inspection reports `Mockito.times()` calls whether they can be optimized or deleted based on their argument values. - `times(0)` can be replaced with `Mockito.never()` for better readability, -- `times(1)` can be removed since 1 is the default value when using `times()` verification. See [Mockito exact verification](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#exact_verification) documentation. +- `times(1)` can be removed since 1 is the default value when using `times()` verification. See [Mockito exact verification](https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#exact_verification) documentation. - `Mockito.times(1)` calls are reported only when they have no sequent calls on them, thus `Mockito.times(1).description("message")` would not be reported. Both of them can be enabled/disabled on the inspection's settings panel. diff --git a/gradle.properties b/gradle.properties index f26ae408..11982ee9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = com.picimako.mockitools pluginName = Mockitools pluginRepositoryUrl = https://github.com/picimako/mockitools # SemVer format -> https://semver.org -pluginVersion = 1.7.0 +pluginVersion = 1.8.0 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 261 diff --git a/src/main/java/com/picimako/mockitools/MockableTypesUtil.java b/src/main/java/com/picimako/mockitools/MockableTypesUtil.java index 19f80b0e..2d90b6f5 100644 --- a/src/main/java/com/picimako/mockitools/MockableTypesUtil.java +++ b/src/main/java/com/picimako/mockitools/MockableTypesUtil.java @@ -30,8 +30,8 @@ public final class MockableTypesUtil { /** * The original logic and set of non-mockable types can be found in Mockito's * */ private static final Set NON_MOCKABLE_TYPES = Set.of(CommonClassNames.JAVA_LANG_CLASS, CommonClassNames.JAVA_LANG_STRING); @@ -49,15 +49,13 @@ public static DoNotMockType getDoNotMockTypeInHierarchy(@Nullable PsiType type) if (referencedClass != null) { //Checks if the use class type is annotated var doNotMock = getDoNotMockAnnotationOn(referencedClass); - if (doNotMock != null) { - return new DoNotMockType(doNotMock); - } + if (doNotMock != null) return new DoNotMockType(doNotMock); + //If the direct class type is not annotated, proceeds to check all its super classes for (PsiClass cls : getSuperClasses(referencedClass)) { var doNotMockInHierarchy = getDoNotMockAnnotationOn(cls); - if (doNotMockInHierarchy != null) { - return new DoNotMockType(doNotMockInHierarchy); - } + if (doNotMockInHierarchy != null) return new DoNotMockType(doNotMockInHierarchy); + } } } diff --git a/src/main/java/com/picimako/mockitools/MockitoMockMatchers.java b/src/main/java/com/picimako/mockitools/MockitoMockMatchers.java index c33c5071..da6ede84 100644 --- a/src/main/java/com/picimako/mockitools/MockitoMockMatchers.java +++ b/src/main/java/com/picimako/mockitools/MockitoMockMatchers.java @@ -8,7 +8,9 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_ANSWER; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK_SETTINGS; +import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE; import static com.picimako.mockitools.MockitoQualifiedNames.SPY; +import static com.picimako.mockitools.MockitoolsPsiUtil.INORDER_VERIFY; import static com.picimako.mockitools.MockitoolsPsiUtil.MOCKITO_MOCK; import static com.siyeh.ig.callMatcher.CallMatcher.anyOf; import static com.siyeh.ig.callMatcher.CallMatcher.staticCall; @@ -26,6 +28,11 @@ public final class MockitoMockMatchers { public static final CallMatcher MOCK_WITH_ANSWER = MOCKITO_MOCK.parameterTypes(JAVA_LANG_CLASS, ORG_MOCKITO_ANSWER); public static final CallMatcher MOCK_WITH_SETTINGS = MOCKITO_MOCK.parameterTypes(JAVA_LANG_CLASS, ORG_MOCKITO_MOCK_SETTINGS); + //MockedStatic specific verify() methods are excluded, since BDDMockito has no way to verify MockedStatic + public static final CallMatcher IN_ORDER_VERIFY_NON_MOCKED_STATIC = CallMatcher.anyOf( + INORDER_VERIFY.parameterTypes("T"), + INORDER_VERIFY.parameterTypes("T", ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE)); + //Spy public static final CallMatcher MOCKITO_SPY_T = staticCall(ORG_MOCKITO_MOCKITO, SPY).parameterTypes("T"); diff --git a/src/main/java/com/picimako/mockitools/completion/MockCompletionContributor.java b/src/main/java/com/picimako/mockitools/completion/MockCompletionContributor.java index 33404bb3..a1086147 100644 --- a/src/main/java/com/picimako/mockitools/completion/MockCompletionContributor.java +++ b/src/main/java/com/picimako/mockitools/completion/MockCompletionContributor.java @@ -49,7 +49,7 @@ final class MockCompletionContributor extends CompletionContributor { private static final Pattern VERIFY_PATTERN = Pattern.compile("verifyNoInteractions|verifyNoMoreInteractions|verifyZeroInteractions"); /** - * This capture uses regex based identification of the target method calls because the CallMatcher based + * This capture uses regex-based identification of the target method calls because the CallMatcher-based * solution can't resolve calls to their methods when the completion is triggered in an empty argument list. */ private static final PsiElementPattern.Capture MOCK_OBJECT_PARAMETER_HOLDER_PATTERN = @@ -70,8 +70,10 @@ public boolean accepts(@NotNull PsiMethodCallExpression element, ProcessingConte })); /** - * Using {@link PsiResolveHelper} because neither {@link com.siyeh.ig.psiutils.ImportUtils#isAlreadyStaticallyImported(PsiJavaCodeReferenceElement)} - * nor {@link com.intellij.psi.impl.source.codeStyle.ImportHelper#isAlreadyImported(PsiJavaFile, String)} worked for determining if the method is already imported. + * Using {@link PsiResolveHelper} because neither + * {@link com.siyeh.ig.psiutils.ImportUtils#isAlreadyStaticallyImported(PsiJavaCodeReferenceElement)} + * nor {@link com.intellij.psi.impl.source.codeStyle.ImportHelper#isAlreadyImported(PsiJavaFile, String)} + * worked for determining if the method is already imported. */ private static boolean isMethodDeclaredInMockitoClass(@NotNull PsiMethodCallExpression methodCall) { var referencedMethodCandidates = PsiResolveHelper.getInstance(methodCall.getProject()).getReferencedMethodCandidates(methodCall, false, true); diff --git a/src/main/java/com/picimako/mockitools/CallChainEndsWith.java b/src/main/java/com/picimako/mockitools/dsl/CallChainEndsWith.java similarity index 53% rename from src/main/java/com/picimako/mockitools/CallChainEndsWith.java rename to src/main/java/com/picimako/mockitools/dsl/CallChainEndsWith.java index 920150e1..d6bc8399 100644 --- a/src/main/java/com/picimako/mockitools/CallChainEndsWith.java +++ b/src/main/java/com/picimako/mockitools/dsl/CallChainEndsWith.java @@ -1,21 +1,22 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.dsl; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; import java.util.List; import com.intellij.psi.PsiMethodCallExpression; +import com.picimako.mockitools.CallChainAnalyzer; /** - * Analyzes a call chain whether the penultimate call is a call to a predefined method name, either {@code when()} or {@code given()}, in which the mock - * object, or the call to a mock object is specified. + * Analyzes a call chain whether the penultimate call is a call to a predefined method name, + * either {@code when()} or {@code given()}, in which the mock object or the call to a mock object is specified. */ -public record CallChainEndsWith(String stubbingCallName) implements CallChainAnalyzer { +record CallChainEndsWith(String stubbingCallName) implements CallChainAnalyzer { - public static final CallChainEndsWith ENDS_WITH_WHEN = new CallChainEndsWith("when"); - public static final CallChainEndsWith ENDS_WITH_GIVEN = new CallChainEndsWith("given"); + static final CallChainEndsWith ENDS_WITH_WHEN = new CallChainEndsWith("when"); + static final CallChainEndsWith ENDS_WITH_GIVEN = new CallChainEndsWith("given"); @Override public boolean analyze(List calls) { diff --git a/src/main/java/com/picimako/mockitools/Convention.java b/src/main/java/com/picimako/mockitools/dsl/Convention.java similarity index 95% rename from src/main/java/com/picimako/mockitools/Convention.java rename to src/main/java/com/picimako/mockitools/dsl/Convention.java index 779f6079..31299660 100644 --- a/src/main/java/com/picimako/mockitools/Convention.java +++ b/src/main/java/com/picimako/mockitools/dsl/Convention.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.dsl; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INORDER; diff --git a/src/main/java/com/picimako/mockitools/dsl/MockAnnotation.java b/src/main/java/com/picimako/mockitools/dsl/MockAnnotation.java index 57bbac54..f07aadf3 100644 --- a/src/main/java/com/picimako/mockitools/dsl/MockAnnotation.java +++ b/src/main/java/com/picimako/mockitools/dsl/MockAnnotation.java @@ -20,7 +20,7 @@ public final class MockAnnotation { * Returns whether the provided boolean attribute is set to true (thus enabled) on the {@code mockAnnotation}. *

* This method doesn't check if the provided annotation is actually a {@code @Mock} one, that check has to be done - * separately, before calling this method. + * separately before calling this method. * * @param mockAnnotation the @Mock annotation on a field * @param attributeName the name of a boolean-value attribute on the @Mock annotation. diff --git a/src/main/java/com/picimako/mockitools/dsl/MockObject.java b/src/main/java/com/picimako/mockitools/dsl/MockObject.java index c45fcc62..c56eeba2 100644 --- a/src/main/java/com/picimako/mockitools/dsl/MockObject.java +++ b/src/main/java/com/picimako/mockitools/dsl/MockObject.java @@ -21,7 +21,7 @@ import java.util.Optional; /** - * Represents a mock variable regardless if it is a local variable or a field. + * Represents a mock variable regardless of it being a local variable or a field. * * @since 0.8.0 */ diff --git a/src/main/java/com/picimako/mockitools/StubType.java b/src/main/java/com/picimako/mockitools/dsl/StubType.java similarity index 95% rename from src/main/java/com/picimako/mockitools/StubType.java rename to src/main/java/com/picimako/mockitools/dsl/StubType.java index bd12f345..e63f12a7 100644 --- a/src/main/java/com/picimako/mockitools/StubType.java +++ b/src/main/java/com/picimako/mockitools/dsl/StubType.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.dsl; /** * Represents the type of stubbing. Naming is according the Mockito naming conventions. diff --git a/src/main/java/com/picimako/mockitools/StubbingApproach.java b/src/main/java/com/picimako/mockitools/dsl/StubbingApproach.java similarity index 89% rename from src/main/java/com/picimako/mockitools/StubbingApproach.java rename to src/main/java/com/picimako/mockitools/dsl/StubbingApproach.java index 4d4a5079..8524bb8b 100644 --- a/src/main/java/com/picimako/mockitools/StubbingApproach.java +++ b/src/main/java/com/picimako/mockitools/dsl/StubbingApproach.java @@ -1,11 +1,10 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.dsl; -import static com.google.common.collect.Iterables.getLast; import static com.intellij.openapi.application.ReadAction.computeBlocking; -import static com.picimako.mockitools.CallChainEndsWith.ENDS_WITH_GIVEN; -import static com.picimako.mockitools.CallChainEndsWith.ENDS_WITH_WHEN; +import static com.intellij.openapi.util.text.StringUtil.substringAfterLast; +import static com.intellij.util.containers.ContainerUtil.map2Set; import static com.picimako.mockitools.MockitoQualifiedNames.DO_THROW; import static com.picimako.mockitools.MockitoQualifiedNames.GIVEN; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; @@ -19,6 +18,8 @@ import static com.picimako.mockitools.MockitoQualifiedNames.THEN_THROW; import static com.picimako.mockitools.MockitoQualifiedNames.WHEN; import static com.picimako.mockitools.MockitoQualifiedNames.WILL_THROW; +import static com.picimako.mockitools.dsl.CallChainEndsWith.ENDS_WITH_GIVEN; +import static com.picimako.mockitools.dsl.CallChainEndsWith.ENDS_WITH_WHEN; import static com.picimako.mockitools.util.PsiMethodUtil.findCallDownwardsInChain; import static com.picimako.mockitools.util.PsiMethodUtil.findCallUpwardsInChain; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; @@ -34,11 +35,11 @@ import com.picimako.mockitools.util.PsiMethodUtil; import com.siyeh.ig.callMatcher.CallMatcher; import lombok.Getter; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Stream; /** @@ -46,14 +47,13 @@ * * @since 0.7.0 */ -@RequiredArgsConstructor @Getter public enum StubbingApproach { /** * Mockito.when().then*() */ - MOCKITO_WHEN(StubType.STUBBING, "when", "Mockito.when()", ORG_MOCKITO_MOCKITO, "Mockito", THEN) { + MOCKITO_WHEN(StubType.STUBBING, "Mockito.when()", ORG_MOCKITO_MOCKITO, "when", THEN) { private final CallMatcher MOCKITO_WHEN = staticCall(ORG_MOCKITO_MOCKITO, WHEN).parameterCount(1); private final CallMatcher MOCKITO_WHEN_THEN_X = instanceCall(ORG_MOCKITO_ONGOING_STUBBING, "then", "thenReturn", "thenThrow", "thenAnswer", "thenCallRealMethod"); @@ -95,7 +95,7 @@ public boolean isValid(PsiMethodCallExpression stubbingCall) { } @Override - public boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { + protected boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { return mockObjectOrCallToStubbedMethod instanceof PsiMethodCallExpression; } @@ -108,7 +108,7 @@ public ExceptionStubber getExceptionStubber() { /** * Mockito.do*().when() */ - MOCKITO_DO_X(StubType.STUBBER, "when", "Mockito.do*()", ORG_MOCKITO_MOCKITO, "Mockito", "do") { + MOCKITO_DO_X(StubType.STUBBER, "Mockito.do*()", ORG_MOCKITO_MOCKITO, "when", "do") { private final CallMatcher MOCKITO_DO_X_WHEN = instanceCall(ORG_MOCKITO_STUBBING_STUBBER, WHEN); private final CallMatcher MOCKITO_DO_X = staticCall(ORG_MOCKITO_MOCKITO, "doReturn", "doThrow", "doAnswer", "doCallRealMethod", "doNothing"); @@ -128,7 +128,7 @@ public Optional getStubbedMethodCall(PsiMethodCallExpre @Override public Optional getStubbedMethodCall(List stubbingCalls) { - return Optional.of(getLast(stubbingCalls)); + return Optional.of(stubbingCalls.getLast()); } @Override @@ -152,7 +152,7 @@ public boolean isValid(List stubbingCalls) { } @Override - public boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { + protected boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { return mockObjectOrCallToStubbedMethod instanceof PsiReferenceExpression; } @@ -165,7 +165,7 @@ public ExceptionStubber getExceptionStubber() { /** * BDDMockito.given().will*() */ - BDDMOCKITO_GIVEN(StubType.STUBBING, "given", "BDDMockito.given()", ORG_MOCKITO_BDDMOCKITO, "BDDMockito", "will") { + BDDMOCKITO_GIVEN(StubType.STUBBING, "BDDMockito.given()", ORG_MOCKITO_BDDMOCKITO, "given", "will") { private final CallMatcher BDDMOCKITO_GIVEN = staticCall(ORG_MOCKITO_BDDMOCKITO, GIVEN).parameterCount(1); private final CallMatcher BDDMOCKITO_GIVEN_WILL_X = instanceCall(ORG_MOCKITO_BDD_MY_ONGOING_STUBBING, "will", "willReturn", "willThrow", "willAnswer", "willCallRealMethod"); @@ -206,7 +206,7 @@ public boolean isValid(PsiMethodCallExpression stubbingCall) { } @Override - public boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { + protected boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { return mockObjectOrCallToStubbedMethod instanceof PsiMethodCallExpression; } @@ -219,7 +219,7 @@ public ExceptionStubber getExceptionStubber() { /** * BDDMockito.will*().given() */ - BDDMOCKITO_WILL_X(StubType.STUBBER, "given", "BDDMockito.will*()", ORG_MOCKITO_BDDMOCKITO, "BDDMockito", "will") { + BDDMOCKITO_WILL_X(StubType.STUBBER, "BDDMockito.will*()", ORG_MOCKITO_BDDMOCKITO, "given", "will") { private final CallMatcher BDDMOCKITO_WILL_X = staticCall(ORG_MOCKITO_BDDMOCKITO, "will", "willReturn", "willThrow", "willAnswer", "willCallRealMethod", "willDoNothing"); private final CallMatcher BDDMOCKITO_WILL_X_GIVEN = @@ -237,7 +237,7 @@ public Optional getStubbedMethodCall(PsiMethodCallExpre @Override public Optional getStubbedMethodCall(List stubbingCalls) { - return Optional.of(getLast(stubbingCalls)); + return Optional.of(stubbingCalls.getLast()); } @Override @@ -261,7 +261,7 @@ public boolean isValid(List stubbingCalls) { } @Override - public boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { + protected boolean isValidStubbingArgument(PsiExpression mockObjectOrCallToStubbedMethod) { return mockObjectOrCallToStubbedMethod instanceof PsiReferenceExpression; } @@ -272,28 +272,35 @@ public ExceptionStubber getExceptionStubber() { } }; - public static final List EXCEPTION_STUBBERS = List.of( - MOCKITO_WHEN.getExceptionStubber(), - MOCKITO_DO_X.getExceptionStubber(), - BDDMOCKITO_GIVEN.getExceptionStubber(), - BDDMOCKITO_WILL_X.getExceptionStubber()); + private static final Set EXCEPTION_STUBBERS = + map2Set(StubbingApproach.values(), StubbingApproach::getExceptionStubber); public final StubType stubType; - public final String methodCallStubber; public final String presentableText; + public final String methodCallStubber; public final String stubStarterClassFqn; - public final String stubStarterClassName; + final String stubStarterClassName; public final String stubPrefix; + StubbingApproach(StubType stubType, String presentableText, String stubStarterClassFqn, String methodCallStubber, String stubPrefix) { + this.stubType = stubType; + this.presentableText = presentableText; + this.methodCallStubber = methodCallStubber; + this.stubStarterClassFqn = stubStarterClassFqn; + this.stubStarterClassName = substringAfterLast(stubStarterClassFqn, "."); + this.stubPrefix = stubPrefix; + } + /** * For example: *

+     * {@code
      * //Returns 'mock.doSomething()' if 'Mockito.when(mock.doSomething())' is passed in
      * Mockito.when(mock.doSomething()).thenReturn(10);
      *
      * //Returns 'mock.doSomething()' if 'given(mock)' is passed in
      * BDDMockito.willReturn(10).given(mock).doSomething();
-     * 
+ * } * * @param methodCallStubber the call in a stubbing call chain that accepts the mock object, * or the method call on a mock object that is being stubbed @@ -307,11 +314,12 @@ public ExceptionStubber getExceptionStubber() { *

* For example: *

+     * {@code
      * //Returns 'mock.doSomething()' is both cases:
      *
      * Mockito.when(mock.doSomething()).thenReturn(10);
      * BDDMockito.willReturn(10).given(mock).doSomething();
-     * 
+ * } * * @param stubbingCalls the calls in the call chain constituting as a stubbing * @return the found call as PsiMethodCallExpression, or empty Optional if the call is not found, or the stubbing @@ -336,9 +344,10 @@ public ExceptionStubber getExceptionStubber() { *

* For example: *

+     * {@code
      * Mockito.when(mock.doSomething()).thenReturn(10); //returns true if 'Mockito.when(mock.doSomething())' is passed in
      * BDDMockito.willReturn(10).given(mock).doSomething(); //return true if 'given(mock)' is passed in
-     * 
+ * } * * @param expression the method accepting the mock object, or the call to the stubbed method */ diff --git a/src/main/java/com/picimako/mockitools/VerificationApproach.java b/src/main/java/com/picimako/mockitools/dsl/VerificationApproach.java similarity index 93% rename from src/main/java/com/picimako/mockitools/VerificationApproach.java rename to src/main/java/com/picimako/mockitools/dsl/VerificationApproach.java index 07160e54..91676bfd 100644 --- a/src/main/java/com/picimako/mockitools/VerificationApproach.java +++ b/src/main/java/com/picimako/mockitools/dsl/VerificationApproach.java @@ -1,8 +1,9 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.dsl; import static com.intellij.openapi.application.ReadAction.computeBlocking; +import static com.picimako.mockitools.MockitoMockMatchers.IN_ORDER_VERIFY_NON_MOCKED_STATIC; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO_THEN; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INORDER; @@ -10,7 +11,6 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKED_STATIC_VERIFICATION; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE; import static com.picimako.mockitools.MockitoQualifiedNames.THEN; -import static com.picimako.mockitools.MockitoQualifiedNames.VERIFY; import static com.picimako.mockitools.MockitoolsPsiUtil.isMockitoVerify; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; import static com.picimako.mockitools.util.PsiMethodUtil.getSubsequentMethodCall; @@ -21,7 +21,7 @@ import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.inspection.stubbing.EnforceConventionInspection; +import com.picimako.mockitools.MockitoolsPsiUtil; import com.siyeh.ig.callMatcher.CallMatcher; import lombok.RequiredArgsConstructor; @@ -94,7 +94,7 @@ public Optional getVerifiedMethodCall(List EnforceConventionInspection.IN_ORDER_VERIFY_NON_MOCKED_STATIC.matches(expression)); + return computeBlocking(() -> IN_ORDER_VERIFY_NON_MOCKED_STATIC.matches(expression)); } @Override @@ -114,8 +114,8 @@ public Optional getVerifiedMethodCall(List getVerifiedMethodCall(ListResetting mocks + * @see Resetting mocks * @since 0.1.0 */ -final class CallOnMockitoResetInspection extends MockitoolsBaseInspection { +final class CallOnMockitoResetInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/util/EvaluationHelper.java b/src/main/java/com/picimako/mockitools/inspection/EvaluationHelper.java similarity index 94% rename from src/main/java/com/picimako/mockitools/util/EvaluationHelper.java rename to src/main/java/com/picimako/mockitools/inspection/EvaluationHelper.java index 03ec8c8f..df9a251e 100644 --- a/src/main/java/com/picimako/mockitools/util/EvaluationHelper.java +++ b/src/main/java/com/picimako/mockitools/inspection/EvaluationHelper.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools.util; +package com.picimako.mockitools.inspection; import static com.picimako.mockitools.util.ClassObjectAccessUtil.getOperandType; @@ -9,6 +9,7 @@ import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiNewExpression; import com.intellij.psi.PsiType; +import com.picimako.mockitools.util.ClassObjectAccessUtil; import org.jetbrains.annotations.Nullable; /** diff --git a/src/main/java/com/picimako/mockitools/inspection/MockitoolsBaseInspection.java b/src/main/java/com/picimako/mockitools/inspection/MockitoolsInspectionBase.java similarity index 88% rename from src/main/java/com/picimako/mockitools/inspection/MockitoolsBaseInspection.java rename to src/main/java/com/picimako/mockitools/inspection/MockitoolsInspectionBase.java index c6585921..32e0f35a 100644 --- a/src/main/java/com/picimako/mockitools/inspection/MockitoolsBaseInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/MockitoolsInspectionBase.java @@ -2,7 +2,7 @@ package com.picimako.mockitools.inspection; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.LocalInspectionToolSession; @@ -19,13 +19,16 @@ /** * Base class for inspections that has to distinguish validation between files in test sources and actual unit test classes. */ -public abstract class MockitoolsBaseInspection extends LocalInspectionTool { +public abstract class MockitoolsInspectionBase extends LocalInspectionTool { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { return isInTestSourceContent(session.getFile()) ? methodCallVisitor(holder) : PsiElementVisitor.EMPTY_VISITOR; } + /** + * Creates a Java element visitor that visits only {@code PsiMethodCallExpression} nodes. + */ @NotNull protected JavaElementVisitor methodCallVisitor(@NotNull ProblemsHolder holder) { return new JavaElementVisitor() { @@ -46,6 +49,9 @@ public void visitNewExpression(@NotNull PsiNewExpression expression) { }; } + /** + * Creates a Java element visitor that visits only {@code PsiField} nodes. + */ @NotNull protected JavaElementVisitor fieldVisitor(@NotNull ProblemsHolder holder) { return new JavaElementVisitor() { @@ -56,6 +62,9 @@ public void visitField(@NotNull PsiField field) { }; } + /** + * Creates a Java element visitor that visits only {@code PsiField} and {@code PsiMethodCallExpression} nodes. + */ @NotNull protected JavaElementVisitor fieldAndMethodCallVisitor(@NotNull ProblemsHolder holder) { return new JavaElementVisitor() { @@ -81,6 +90,9 @@ public void visitNewExpression(@NotNull PsiNewExpression expression) { }; } + /** + * Creates a Java element visitor that visits only {@code PsiAnnotation} and {@code PsiMethodCallExpression} nodes. + */ @NotNull protected JavaElementVisitor annotationAndMethodCallVisitor(@NotNull ProblemsHolder holder) { return new JavaElementVisitor() { diff --git a/src/main/java/com/picimako/mockitools/util/PointersUtil.java b/src/main/java/com/picimako/mockitools/inspection/PointersUtil.java similarity index 79% rename from src/main/java/com/picimako/mockitools/util/PointersUtil.java rename to src/main/java/com/picimako/mockitools/inspection/PointersUtil.java index b48eb1ea..31b80dca 100644 --- a/src/main/java/com/picimako/mockitools/util/PointersUtil.java +++ b/src/main/java/com/picimako/mockitools/inspection/PointersUtil.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools.util; +package com.picimako.mockitools.inspection; import static com.intellij.util.containers.ContainerUtil.map; @@ -16,10 +16,17 @@ */ public final class PointersUtil { + /** + * Converts all items in {@code calls} to a {@code SmartPsiElementPointer}. + */ + @NotNull public static List> toPointers(List calls) { return map(calls, PointersUtil::toPointer); } + /** + * Converts the provided {@code element} to a {@code SmartPsiElementPointer}. + */ @NotNull public static SmartPsiElementPointer toPointer(T element) { return SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element, element.getContainingFile()); diff --git a/src/main/java/com/picimako/mockitools/inspection/BaseQuickFix.java b/src/main/java/com/picimako/mockitools/inspection/QuickFixBase.java similarity index 95% rename from src/main/java/com/picimako/mockitools/inspection/BaseQuickFix.java rename to src/main/java/com/picimako/mockitools/inspection/QuickFixBase.java index 9ee79651..1a1cd2ef 100644 --- a/src/main/java/com/picimako/mockitools/inspection/BaseQuickFix.java +++ b/src/main/java/com/picimako/mockitools/inspection/QuickFixBase.java @@ -13,7 +13,7 @@ /** * Base implementation for quick fixes. Based on the original {@code com.siyeh.ig.InspectionGadgetsFix}. */ -public abstract class BaseQuickFix implements LocalQuickFix { +public abstract class QuickFixBase implements LocalQuickFix { public final void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { var problemElement = descriptor.getPsiElement(); diff --git a/src/main/java/com/picimako/mockitools/util/UnitTestPsiUtil.java b/src/main/java/com/picimako/mockitools/inspection/UnitTestPsiUtil.java similarity index 96% rename from src/main/java/com/picimako/mockitools/util/UnitTestPsiUtil.java rename to src/main/java/com/picimako/mockitools/inspection/UnitTestPsiUtil.java index 8397a56f..f3ee6a1c 100644 --- a/src/main/java/com/picimako/mockitools/util/UnitTestPsiUtil.java +++ b/src/main/java/com/picimako/mockitools/inspection/UnitTestPsiUtil.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools.util; +package com.picimako.mockitools.inspection; import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.PsiElement; diff --git a/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspection.java b/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspection.java index ad87a9f5..81e17b34 100644 --- a/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspection.java @@ -4,7 +4,7 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_CAPTOR; import static com.picimako.mockitools.MockitoolsPsiUtil.isOfTypeArgumentCaptor; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemDescriptor; @@ -15,8 +15,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiField; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.QuickFixBase; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import org.jetbrains.annotations.NotNull; import com.picimako.mockitools.resources.MockitoolsBundle; @@ -35,7 +35,7 @@ * @see Baeldung - Mockito Annotations * @since 0.1.0 */ -final class CaptorFieldInitializationInspection extends MockitoolsBaseInspection { +final class CaptorFieldInitializationInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { @@ -50,7 +50,7 @@ protected void checkField(PsiField field, @NotNull ProblemsHolder holder) { } } - private static final class RemoveArgumentCaptorInitQuickFix extends BaseQuickFix { + private static final class RemoveArgumentCaptorInitQuickFix extends QuickFixBase { @Override public @IntentionName @NotNull String getName() { diff --git a/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspection.java b/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspection.java index b1b1d8b1..6a21d9f9 100644 --- a/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspection.java @@ -5,7 +5,7 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_ARGUMENT_CAPTOR; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_CAPTOR; import static com.picimako.mockitools.MockitoolsPsiUtil.isOfTypeArgumentCaptor; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemDescriptor; @@ -20,8 +20,8 @@ import com.intellij.psi.PsiPrimitiveType; import com.intellij.psi.PsiType; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.QuickFixBase; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -35,7 +35,7 @@ * * @since 0.1.0 */ -final class CaptorFieldOfTypeArgumentCaptorInspection extends MockitoolsBaseInspection { +final class CaptorFieldOfTypeArgumentCaptorInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { @@ -55,7 +55,7 @@ protected void checkField(PsiField field, @NotNull ProblemsHolder holder) { *

* With that said, it imports {@code org.mockito.ArgumentCaptor} upon applying the quick fix. */ - private static final class ConvertFieldTypeToArgumentCaptorQuickFix extends BaseQuickFix { + private static final class ConvertFieldTypeToArgumentCaptorQuickFix extends QuickFixBase { @Override public @IntentionName @NotNull String getName() { diff --git a/src/main/java/com/picimako/mockitools/inspection/captor/GenericInferredCaptorCreationInspection.java b/src/main/java/com/picimako/mockitools/inspection/captor/GenericInferredCaptorCreationInspection.java index d2739e5f..7f678f27 100644 --- a/src/main/java/com/picimako/mockitools/inspection/captor/GenericInferredCaptorCreationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/captor/GenericInferredCaptorCreationInspection.java @@ -10,7 +10,7 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -21,7 +21,7 @@ * @see com.picimako.mockitools.inspection.mocking.GenericInferredMockAndSpyCreationInspection * @since 1.3.0 */ -final class GenericInferredCaptorCreationInspection extends MockitoolsBaseInspection { +final class GenericInferredCaptorCreationInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallAnalyzer.java b/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallAnalyzer.java index 7db320bd..f0a88012 100644 --- a/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallAnalyzer.java +++ b/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallAnalyzer.java @@ -7,12 +7,12 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import com.picimako.mockitools.inspection.stubbing.ExceptionStubber; import com.siyeh.ig.callMatcher.CallMatcher; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; +import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,14 +21,12 @@ */ class ConsecutiveCallAnalyzer { /** - * The method name whose consecutiveness the inspection looks for. - *

- * E.g. {@code doReturn}, {@code thenReturn}, etc. + * The method name whose consecutiveness the inspection looks for. E.g. {@code doReturn}, {@code thenReturn}, etc. */ @NotNull final String consecutiveMethodName; /** - * Whether to skip the analysis of the first call in a call chain, because in case of e.g. {@code Mockito.when()} it is certain that + * Whether to skip the analysis of the first call in a call chain. In case of e.g. {@code Mockito.when()} it is certain that * {@code when()} will never match e.g. {@code thenReturn()}, so we can skip that comparison and start at the next call. */ final boolean skipAnalysisOfFirstCall; @@ -38,7 +36,7 @@ class ConsecutiveCallAnalyzer { @Nullable final ExceptionStubber exceptionStubber; /** - * The call matcher built for {@link Analyzer#inCallChainsBeginningWithStatic}. + * The call matcher built with {@link Analyzer#inCallChainsBeginningWithStatic} and {@link Analyzer#inCallChainsBeginningWithInstance}. */ @NotNull private final CallMatcher chainStarterMethodMatcher; @@ -55,26 +53,25 @@ boolean canAnalyze(PsiMethodCallExpression expression) { } /** - * Builder for {@code ConsecutiveCallAnalyzer}. + * Builder for {@link ConsecutiveCallAnalyzer}. */ - @RequiredArgsConstructor static final class Analyzer { /** * Used in the quick fix as the beginning of the expression that is built for replacement. - *

- * Either {@code org.mockito.Mockito}, {@code org.mockito.BDDMockito} or {@code org.mockito.MockedStatic}. */ private final String mockitoClass; - private CallMatcher chainStarterMethodMatcher; + @NotNull @Accessors(fluent = true) @Setter private String consecutiveMethodName; private boolean skipAnalysisOfFirstCall = false; - /** - * Applicable only in case of {@link SimplifyConsecutiveThrowCallsInspection}. - */ @Nullable private ExceptionStubber exceptionStubbingVia; + private CallMatcher chainStarterMethodMatcher; + + Analyzer(@MagicConstant(stringValues = {ORG_MOCKITO_MOCKITO, ORG_MOCKITO_BDDMOCKITO, ORG_MOCKITO_MOCKED_STATIC}) String mockitoClass) { + this.mockitoClass = mockitoClass; + } /** * Configures the method names as static methods in {@link #mockitoClass}. @@ -99,7 +96,7 @@ Analyzer inCallChainsBeginningWithInstance(String... chainStarterMethodNames) { } /** - * Configures this builder to do skip the analysis of the first call in a chain. + * Configures this builder to skip the analysis of the first call in a chain. */ Analyzer skippingAnalysisOfFirstCall() { skipAnalysisOfFirstCall = true; @@ -107,7 +104,7 @@ Analyzer skippingAnalysisOfFirstCall() { } /** - * Configure the exception stubber from the provide stubbing approach + * Configure the exception stubber from the provided stubbing approach. */ Analyzer exceptionStubbingVia(StubbingApproach stubbingApproach) { exceptionStubbingVia = stubbingApproach.getExceptionStubber(); @@ -120,15 +117,15 @@ ConsecutiveCallAnalyzer build() { //Static factory methods - static Analyzer forMockito(String consecutiveMethodName) { + static Analyzer forMockito(@NotNull String consecutiveMethodName) { return new Analyzer(ORG_MOCKITO_MOCKITO).consecutiveMethodName(consecutiveMethodName); } - static Analyzer forBDDMockito(String consecutiveMethodName) { + static Analyzer forBDDMockito(@NotNull String consecutiveMethodName) { return new Analyzer(ORG_MOCKITO_BDDMOCKITO).consecutiveMethodName(consecutiveMethodName); } - static Analyzer forMockedStatic(String consecutiveMethodName) { + static Analyzer forMockedStatic(@NotNull String consecutiveMethodName) { return new Analyzer(ORG_MOCKITO_MOCKED_STATIC).consecutiveMethodName(consecutiveMethodName); } } diff --git a/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallRegistrar.java b/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallRegistrar.java index 4df3c02f..98304cbc 100644 --- a/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallRegistrar.java +++ b/src/main/java/com/picimako/mockitools/inspection/consecutive/ConsecutiveCallRegistrar.java @@ -2,14 +2,14 @@ package com.picimako.mockitools.inspection.consecutive; -import static com.google.common.collect.Iterables.getLast; -import static com.picimako.mockitools.util.PointersUtil.toPointers; +import static com.picimako.mockitools.inspection.PointersUtil.toPointers; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.SmartPsiElementPointer; import com.picimako.mockitools.inspection.stubbing.ExceptionStubber; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -20,23 +20,27 @@ class ConsecutiveCallRegistrar { /** * @see ConsecutiveCallAnalyzer#consecutiveMethodName */ + @NotNull final String consecutiveMethodName; /** * The full list of calls in the currently analyzed call chain. */ + @NotNull final List callsInWholeChain; /** * The indeces of the target section of consecutive calls within {@link #callsInWholeChain}. */ + @NotNull final List consecutiveCallIndeces; /** * @see ConsecutiveCallAnalyzer#exceptionStubber */ + @Nullable private final ExceptionStubber exceptionStubber; /** - * The list of {@code callsInWholeChain} as {@link SmartPsiElementPointer}s used within the respective quick fix. + * The list of {@link callsInWholeChain} as {@link SmartPsiElementPointer}s used within the respective quick fix. *

- * This is lazy-initialized, and instantiated only when {@code MergeConsecutiveStubbingCallsQuickFix} is actually called. + * This is lazy-initialized and instantiated only when {@link MergeConsecutiveStubbingCallsQuickFix} is actually called. */ private List> wholeChainPointers = null; @@ -53,7 +57,7 @@ class ConsecutiveCallRegistrar { * Returns the last consecutive call from the current section of calls. */ PsiMethodCallExpression getLastConsecutiveCall() { - return callsInWholeChain.get(getLast(consecutiveCallIndeces)); + return callsInWholeChain.get(consecutiveCallIndeces.getLast()); } boolean isCallToClasses(PsiMethodCallExpression call) { @@ -66,16 +70,25 @@ boolean isCallToThrowables(PsiMethodCallExpression call) { //For quick fixes + /** + * Returns the {@code index}th method call from the call chain. + */ PsiMethodCallExpression getElement(int index) { initializePointersIfNotAlready(); return wholeChainPointers.get(index).getElement(); } + /** + * Returns the first consecutive method call from the call chain. + */ PsiMethodCallExpression getFirstConsecutiveCall() { initializePointersIfNotAlready(); return wholeChainPointers.get(consecutiveCallIndeces.getFirst()).getElement(); } + /** + * Returns the file the current registrar is working with. + */ VirtualFile getContainingFile() { initializePointersIfNotAlready(); return wholeChainPointers.getFirst().getVirtualFile(); diff --git a/src/main/java/com/picimako/mockitools/inspection/consecutive/MergeConsecutiveStubbingCallsQuickFix.java b/src/main/java/com/picimako/mockitools/inspection/consecutive/MergeConsecutiveStubbingCallsQuickFix.java index ed330be1..8295ae09 100644 --- a/src/main/java/com/picimako/mockitools/inspection/consecutive/MergeConsecutiveStubbingCallsQuickFix.java +++ b/src/main/java/com/picimako/mockitools/inspection/consecutive/MergeConsecutiveStubbingCallsQuickFix.java @@ -11,7 +11,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; -import com.picimako.mockitools.inspection.BaseQuickFix; +import com.picimako.mockitools.inspection.QuickFixBase; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -19,10 +19,10 @@ /** * Quick fix that merges consecutive {@code *Throw()} or {@code *Return()} calls, respectively. - * Only a single section of consecutive calls is merged if there are multiple. + * Only a single section of consecutive calls is merged if there are multiple ones. */ @RequiredArgsConstructor -public class MergeConsecutiveStubbingCallsQuickFix extends BaseQuickFix { +public class MergeConsecutiveStubbingCallsQuickFix extends QuickFixBase { private final ConsecutiveCallRegistrar registrar; private final TypeConversionMethod argumentTypeConverter; diff --git a/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveCallsInspectionBase.java b/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveCallsInspectionBase.java index 8ade88d1..8e90bd66 100644 --- a/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveCallsInspectionBase.java +++ b/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveCallsInspectionBase.java @@ -3,6 +3,7 @@ package com.picimako.mockitools.inspection.consecutive; import static com.intellij.util.containers.ContainerUtil.find; +import static com.intellij.util.containers.ContainerUtil.map2Array; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.getReferenceNameElement; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; @@ -11,11 +12,10 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.util.SmartList; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; import java.util.List; import java.util.function.Predicate; @@ -29,7 +29,11 @@ * {@link #register} -> * {@link #doRegister} */ -abstract class SimplifyConsecutiveCallsInspectionBase extends MockitoolsBaseInspection { +abstract class SimplifyConsecutiveCallsInspectionBase extends MockitoolsInspectionBase { + + protected abstract List analyzers(); + + protected abstract void register(ConsecutiveCallRegistrar context, @NotNull ProblemsHolder holder); //Analysis workflow @@ -74,22 +78,20 @@ protected Predicate extraCondition() { return call -> true; } - protected abstract List analyzers(); - //Registration workflow - private void registerMultiple(ConsecutiveCallAnalyzer analyzer, List callsInWholeChain, - List consecutiveCallIndeces, @NotNull ProblemsHolder holder) { + private void registerMultiple(ConsecutiveCallAnalyzer analyzer, + List callsInWholeChain, + List consecutiveCallIndeces, + @NotNull ProblemsHolder holder) { if (consecutiveCallIndeces.size() > 1) { register(new ConsecutiveCallRegistrar(analyzer, callsInWholeChain, consecutiveCallIndeces), holder); } } - protected abstract void register(ConsecutiveCallRegistrar context, @NotNull ProblemsHolder holder); - protected void doRegister(ConsecutiveCallRegistrar registrar, @NotNull ProblemsHolder holder, TypeConversionMethod... typeConversionMethods) { + var quickFixes = map2Array(typeConversionMethods, LocalQuickFix.class, method -> new MergeConsecutiveStubbingCallsQuickFix(registrar, method)); holder.registerProblem(getReferenceNameElement(registrar.getLastConsecutiveCall()), - MockitoolsBundle.message("inspection.can.merge.with.previous.consecutive.calls", registrar.consecutiveMethodName), - Arrays.stream(typeConversionMethods).map(method -> new MergeConsecutiveStubbingCallsQuickFix(registrar, method)).toArray(LocalQuickFix[]::new)); + MockitoolsBundle.message("inspection.can.merge.with.previous.consecutive.calls", registrar.consecutiveMethodName), quickFixes); } } diff --git a/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspection.java b/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspection.java index 3d42268c..ee228346 100644 --- a/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspection.java @@ -9,10 +9,10 @@ import static com.picimako.mockitools.MockitoQualifiedNames.WHEN; import static com.picimako.mockitools.MockitoQualifiedNames.WILL_RETURN; import static com.picimako.mockitools.MockitoQualifiedNames.WILL_THROW; -import static com.picimako.mockitools.StubbingApproach.BDDMOCKITO_GIVEN; -import static com.picimako.mockitools.StubbingApproach.BDDMOCKITO_WILL_X; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_DO_X; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_WHEN; +import static com.picimako.mockitools.dsl.StubbingApproach.BDDMOCKITO_GIVEN; +import static com.picimako.mockitools.dsl.StubbingApproach.BDDMOCKITO_WILL_X; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_DO_X; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_WHEN; import static com.picimako.mockitools.util.PsiMethodUtil.containsCallToNonDefaultConstructor; import static com.picimako.mockitools.util.PsiMethodUtil.getArguments; diff --git a/src/main/java/com/picimako/mockitools/inspection/consecutive/TypeConversionMethod.java b/src/main/java/com/picimako/mockitools/inspection/consecutive/TypeConversionMethod.java index 0e802def..33e5e6d5 100644 --- a/src/main/java/com/picimako/mockitools/inspection/consecutive/TypeConversionMethod.java +++ b/src/main/java/com/picimako/mockitools/inspection/consecutive/TypeConversionMethod.java @@ -14,7 +14,7 @@ /** * Defines the conversion from {@link PsiClassObjectAccessExpression}s to {@link PsiNewExpression}s and vice versa. *

- * It is used when users have multiple choices how to merge the *Throw() calls together. + * It is used when users have multiple choices for how to merge the *Throw() calls. */ @RequiredArgsConstructor public enum TypeConversionMethod { diff --git a/src/main/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspection.java b/src/main/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspection.java index b61e293c..8b4e8a19 100644 --- a/src/main/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspection.java @@ -19,11 +19,12 @@ import java.util.Optional; /** - * Reports any types of classes or interfaces that extend a class or at least one interface annotated as Mockito's {@code NotExtensible} annotation. + * Reports any types of classes/interfaces that extend a class or implements at least one interface annotated as + * Mockito's {@code NotExtensible} annotation. *

- * This inspection is meant for Mockito framework integrators, not regular end-users, this is disabled by default in the plugin.xml. + * This inspection is meant for Mockito framework integrators, not regular end-users. This is disabled by default in the plugin.xml. * - * @see Advanced public API for framework integrations (Since 2.10.+) + * @see Advanced public API for framework integrations (Since 2.10.+) * @see @NotExtensible javadoc * @since 0.1.0 */ @@ -34,14 +35,6 @@ final class NotExtensibleClassInspection extends LocalInspectionTool { return new JavaElementVisitor() { @Override public void visitClass(@NotNull PsiClass aClass) { - if (aClass.isAnnotationType() - || aClass.isEnum() - || aClass.isRecord() - || aClass instanceof PsiTypeParameter - || aClass instanceof PsiAnonymousClass - || aClass.getExtendsList() == null) { - return; - } if (aClass.isInterface()) { var isAnySuperInterfaceHasNotExtensible = exists( aClass.getExtendsListTypes(), @@ -51,7 +44,7 @@ public void visitClass(@NotNull PsiClass aClass) { //at this point the name identifier should not be null holder.registerProblem(aClass.getNameIdentifier(), MockitoolsBundle.message("inspection.interface.extends.not.extensible")); } - } else { + } else if (isRegularClassWithSuperClass(aClass)) { var superClass = aClass.getSuperClass(); if (superClass != null && superClass.hasAnnotation(ORG_MOCKITO_NOT_EXTENSIBLE)) { //at this point the name identifier should not be null @@ -61,4 +54,13 @@ public void visitClass(@NotNull PsiClass aClass) { } }; } + + private boolean isRegularClassWithSuperClass(PsiClass aClass) { + return !aClass.isAnnotationType() + && !aClass.isEnum() + && !aClass.isRecord() + && !(aClass instanceof PsiTypeParameter) + && !(aClass instanceof PsiAnonymousClass) + && aClass.getExtendsList() != null; + } } diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ArgumentMatchersCalledViaMatchersInspection.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ArgumentMatchersCalledViaMatchersInspection.java index ad07f656..9aeb16f0 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ArgumentMatchersCalledViaMatchersInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ArgumentMatchersCalledViaMatchersInspection.java @@ -2,35 +2,22 @@ package com.picimako.mockitools.inspection.migrationaids.v4; -import static com.intellij.psi.search.GlobalSearchScope.moduleWithLibrariesScope; -import static com.picimako.mockitools.MockitoQualifiedNames.ARGUMENT_MATCHERS; -import static com.picimako.mockitools.MockitoQualifiedNames.MATCHERS; -import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_ARGUMENT_MATCHERS; -import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MATCHERS; import static com.picimako.mockitools.MockitoolsPsiUtil.isMatchers; import static com.picimako.mockitools.util.PsiMethodUtil.getQualifier; -import static com.siyeh.ig.psiutils.ImportUtils.nameCanBeImported; - -import java.util.Optional; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.util.IntentionName; -import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; -import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementFactory; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiMethodCallExpression; -import com.siyeh.ig.psiutils.ImportUtils; -import org.jetbrains.annotations.NotNull; - import com.picimako.mockitools.resources.MockitoolsBundle; +import org.jetbrains.annotations.NotNull; /** - * Reports usages of argument matchers called via {@code org.mockito.Matchers}. That class is deprecated since - * Mockito v2, and is removed in v4. + * Reports usages of argument matchers called via {@code org.mockito.Matchers}. + * That class is deprecated since Mockito v2 and is removed in v4. *

* It also provides a quick fix to replace the qualifier of the method call and use {@code org.mockito.ArgumentMatchers}. * @@ -46,7 +33,7 @@ protected void checkMethodCallExpression(PsiMethodCallExpression expression, @No } } - public static final class ReplaceMatchersWithArgumentMatchersQuickFix extends MigrationAidV4BaseQuickFix { + public static final class ReplaceMatchersWithArgumentMatchersQuickFix extends MigrationAidV4QuickFix { @Override public @IntentionName @NotNull String getName() { return MockitoolsBundle.message("quick.fix.migration.aid.v4.use.argument.matchers.instead.of.matchers"); @@ -54,31 +41,7 @@ public static final class ReplaceMatchersWithArgumentMatchersQuickFix extends Mi @Override protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor, PsiElement problemElement) { - replace((PsiExpression) problemElement, project); - } - - void replace(PsiExpression qualifier, @NotNull Project project) { - //if the matcher is referenced with fully qualified name: replace the fqn appropriately - //This also respects that the class is referenced by its fqn rather via import by keeping the fqn of ArgumentMatchers. - if (qualifier.textMatches(ORG_MOCKITO_MATCHERS)) { - replaceQualifierWith(ORG_MOCKITO_ARGUMENT_MATCHERS, qualifier); - } - //if the matcher is referenced as Matchers.: - // then replace the classname (the qualifier) with ArgumentMatchers, - // add import statement for org.mockito.ArgumentMatchers if necessary - else if (qualifier.textMatches(MATCHERS)) { - PsiElement elementAfterReplace = replaceQualifierWith(ARGUMENT_MATCHERS, qualifier); - if (nameCanBeImported(ORG_MOCKITO_ARGUMENT_MATCHERS, elementAfterReplace)) { - //Module scope is used because different modules may use different versions of Mockito - Optional.ofNullable(ModuleUtilCore.findModuleForFile(elementAfterReplace.getContainingFile().getVirtualFile(), project)) - .map(module -> JavaPsiFacade.getInstance(project).findClass(ORG_MOCKITO_ARGUMENT_MATCHERS, moduleWithLibrariesScope(module))) - .ifPresent(argumentMatchers -> ImportUtils.addImportIfNeeded(argumentMatchers, elementAfterReplace)); - } - } - } - - private PsiElement replaceQualifierWith(String replacement, PsiExpression qualifier) { - return qualifier.replace(PsiElementFactory.getInstance(qualifier.getProject()).createExpressionFromText(replacement, qualifier)); + QualifierReplacements.replaceMatchersWithArgumentMatchers((PsiExpression) problemElement, project); } } } diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidBase.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidBase.java index 6e037af4..0f845f32 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidBase.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidBase.java @@ -2,17 +2,16 @@ package com.picimako.mockitools.inspection.migrationaids.v4; -import static com.picimako.mockitools.util.ModuleDependencyHelper.isMockitoCore2xOr3xAvailableInModuleOf; -import static com.picimako.mockitools.util.ModuleDependencyHelper.isMockitoCore3xAvailableInModuleOf; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.migrationaids.v4.ModuleDependencyHelper.isMockitoCore2xOr3xAvailableInModuleOf; +import static com.picimako.mockitools.inspection.migrationaids.v4.ModuleDependencyHelper.isMockitoCore3xAvailableInModuleOf; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiElementVisitor; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import org.jetbrains.annotations.NotNull; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; - /** * Provides base classes for migration aid inspections. */ @@ -21,7 +20,7 @@ public interface MigrationAidBase { /** * Migration from 3.x to 4.x. */ - abstract class V3ToV4BaseInspection extends MockitoolsBaseInspection { + abstract class V3ToV4BaseInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { return isInTestSourceContent(session.getFile()) && isMockitoCore3xAvailableInModuleOf(session.getFile(), holder.getProject()) ? methodCallVisitor(holder) : PsiElementVisitor.EMPTY_VISITOR; @@ -31,7 +30,7 @@ abstract class V3ToV4BaseInspection extends MockitoolsBaseInspection { /** * Migration from 2.x or 3.x to 4.x. */ - abstract class V23ToV4BaseInspection extends MockitoolsBaseInspection { + abstract class V23ToV4BaseInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { return isInTestSourceContent(session.getFile()) && isMockitoCore2xOr3xAvailableInModuleOf(session.getFile(), holder.getProject()) ? methodCallVisitor(holder) : PsiElementVisitor.EMPTY_VISITOR; diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidV4BaseQuickFix.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidV4QuickFix.java similarity index 75% rename from src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidV4BaseQuickFix.java rename to src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidV4QuickFix.java index 314c5f15..6fcf59be 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidV4BaseQuickFix.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/MigrationAidV4QuickFix.java @@ -9,15 +9,14 @@ import com.intellij.codeInspection.util.IntentionFamilyName; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.migrationaids.v4.ArgumentMatchersCalledViaMatchersInspection.ReplaceMatchersWithArgumentMatchersQuickFix; +import com.picimako.mockitools.inspection.QuickFixBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; /** * Base class for Mockito v4 migration aid quick fixes. */ -public abstract class MigrationAidV4BaseQuickFix extends BaseQuickFix { +abstract class MigrationAidV4QuickFix extends QuickFixBase { @Override public @IntentionFamilyName @NotNull String getFamilyName() { @@ -25,8 +24,8 @@ public abstract class MigrationAidV4BaseQuickFix extends BaseQuickFix { } /** - * Replaces org.mockito.Matchers qualifiers with org.mockito.ArgumentMatchers and static imports the matcher - * that is provided in {@code matcherToImport}. + * Replaces {@code org.mockito.Matchers} qualifiers with {@code org.mockito.ArgumentMatchers} and + * static-imports the matcher that is provided in {@code matcherToImport}. */ protected void replaceMatchersQualifierAndStaticImportMatcher(PsiMethodCallExpression parentCall, String matcherToImport, PsiElement context) { if (!replaceMatchersQualifier(parentCall) && !isAlreadyStaticallyImported(parentCall.getMethodExpression())) { @@ -44,11 +43,12 @@ protected void staticImport(PsiMethodCallExpression parentCall, String clazz, St } /** - * Returns whether the argument method call has a qualifier or not. + * Replaces {@code org.mockito.Matchers} qualifiers with {@code org.mockito.ArgumentMatchers}. + * Returns whether the argument method call had a qualifier. */ protected boolean replaceMatchersQualifier(PsiMethodCallExpression parentCall) { if (parentCall.getMethodExpression().isQualified()) { - new ReplaceMatchersWithArgumentMatchersQuickFix().replace(getQualifier(parentCall), parentCall.getProject()); + QualifierReplacements.replaceMatchersWithArgumentMatchers(getQualifier(parentCall), parentCall.getProject()); return true; } return false; diff --git a/src/main/java/com/picimako/mockitools/util/ModuleDependencyHelper.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ModuleDependencyHelper.java similarity index 53% rename from src/main/java/com/picimako/mockitools/util/ModuleDependencyHelper.java rename to src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ModuleDependencyHelper.java index a19ecf03..9ee8183a 100644 --- a/src/main/java/com/picimako/mockitools/util/ModuleDependencyHelper.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ModuleDependencyHelper.java @@ -1,54 +1,50 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools.util; +package com.picimako.mockitools.inspection.migrationaids.v4; -import java.util.Arrays; - -import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEnumerator; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; +import java.util.function.Predicate; + /** * Utility for working with module dependencies. * * @see How do I get dependencies and classpath of a module? */ -public final class ModuleDependencyHelper { - +@NoArgsConstructor(access = AccessLevel.PRIVATE) +final class ModuleDependencyHelper { private static final String MOCKITO_CORE_2_X = "mockito-core-2."; private static final String MOCKITO_CORE_3_X = "mockito-core-3."; /** * Returns whether mockito-core-3.x is available in the module where the argument file is located. */ - public static boolean isMockitoCore3xAvailableInModuleOf(@NotNull PsiFile file, Project project) { - Module module = ModuleUtilCore.findModuleForFile(file.getVirtualFile(), project); - return module != null && Arrays.stream(OrderEnumerator.orderEntries(module) - .librariesOnly() - .compileOnly() - .classes() - .usingCache() - .getRoots()) - .anyMatch(lib -> lib.getName().startsWith(MOCKITO_CORE_3_X)); + static boolean isMockitoCore3xAvailableInModuleOf(@NotNull PsiFile file, Project project) { + return isMockitoXAvailableInModuleOf(file, project, lib -> lib.getName().startsWith(MOCKITO_CORE_3_X)); } /** * Returns whether mockito-core-2.x or mockito-core-3.x is available in the module where the argument file is located. */ - public static boolean isMockitoCore2xOr3xAvailableInModuleOf(@NotNull PsiFile file, Project project) { - Module module = ModuleUtilCore.findModuleForFile(file.getVirtualFile(), project); + static boolean isMockitoCore2xOr3xAvailableInModuleOf(@NotNull PsiFile file, Project project) { + return isMockitoXAvailableInModuleOf(file, project, lib -> lib.getName().startsWith(MOCKITO_CORE_2_X) || lib.getName().startsWith(MOCKITO_CORE_3_X)); + } + + private static boolean isMockitoXAvailableInModuleOf(@NotNull PsiFile file, Project project, Predicate isMatchingMockitoLib) { + var module = ModuleUtilCore.findModuleForFile(file.getVirtualFile(), project); return module != null && Arrays.stream(OrderEnumerator.orderEntries(module) .librariesOnly() .compileOnly() .classes() - .usingCache() //to improve performance, since this check is executed at the beginning of migration aid inspections + .usingCache() .getRoots()) - .anyMatch(lib -> lib.getName().startsWith(MOCKITO_CORE_2_X) || lib.getName().startsWith(MOCKITO_CORE_3_X)); - } - - private ModuleDependencyHelper() { - //Utility class + .anyMatch(isMatchingMockitoLib); } } diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionAwareReferenceReplacerQuickFix.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionAwareReferenceReplacerQuickFix.java index 792d0072..489aea9f 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionAwareReferenceReplacerQuickFix.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionAwareReferenceReplacerQuickFix.java @@ -2,13 +2,7 @@ package com.picimako.mockitools.inspection.migrationaids.v4; -import static com.intellij.psi.JavaPsiFacade.getElementFactory; -import static com.intellij.psi.search.GlobalSearchScope.moduleWithLibrariesScope; - -import java.util.Optional; - import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.util.IntentionName; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; @@ -16,31 +10,23 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiJavaCodeReferenceElement; import com.intellij.psi.PsiJavaFile; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; -import com.picimako.mockitools.resources.MockitoolsBundle; -import org.jetbrains.annotations.PropertyKey; - /** - * Replaces code reference elements with the predefined class ({@link #replacementClassFqn}). + * Replaces code reference elements with the predefined class ({@link replacementClassFqn}). *

* This quick fix is for locations where it matters whether the qualifier of the reference should remain untouched, * due to a possible name collision between the original and the replacement names. *

* If the reference is qualified, the reference is replaced with the predefined fully qualified name, * otherwise it is the import statement of the reference that is replaced. + * + * @see NameCollisionlessReferenceReplacerQuickFix */ -@RequiredArgsConstructor -public class NameCollisionAwareReferenceReplacerQuickFix extends MigrationAidV4BaseQuickFix { +public final class NameCollisionAwareReferenceReplacerQuickFix extends ReferenceReplacerQuickFix { - @PropertyKey(resourceBundle = "messages.MockitoolsBundle") - private final String nameMessageKey; - private final String replacementClassFqn; - - @Override - public @IntentionName @NotNull String getName() { - return MockitoolsBundle.message(nameMessageKey); + public NameCollisionAwareReferenceReplacerQuickFix(String nameMessageKey, String replacementClassFqn) { + super(nameMessageKey, replacementClassFqn); } @Override @@ -51,14 +37,10 @@ protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descri if (module == null) return; if (element.isQualified()) { - element.replace(getElementFactory(project).createReferenceElementByFQClassName(replacementClassFqn, moduleWithLibrariesScope(module))); + element.replace(createRefElementByFQN(project, module)); } else { - Optional.ofNullable(containingFile.findImportReferenceTo((PsiClass) element.resolve())) - .ifPresent(importStmt -> - importStmt.replace( - getElementFactory(project) - .createFQClassNameReferenceElement(replacementClassFqn, moduleWithLibrariesScope(module)))); + var importReference = containingFile.findImportReferenceTo((PsiClass) element.resolve()); + if (importReference != null) importReference.replace(createClassNameRefElement(project, module)); } } - } diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionlessReferenceReplacerQuickFix.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionlessReferenceReplacerQuickFix.java index 3cdfed3e..b132946c 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionlessReferenceReplacerQuickFix.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/NameCollisionlessReferenceReplacerQuickFix.java @@ -2,43 +2,33 @@ package com.picimako.mockitools.inspection.migrationaids.v4; -import static com.intellij.psi.JavaPsiFacade.getElementFactory; -import static com.intellij.psi.search.GlobalSearchScope.moduleWithLibrariesScope; - -import java.util.Optional; - import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.util.IntentionName; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; -import com.picimako.mockitools.resources.MockitoolsBundle; - /** * Replaces code reference elements with the predefined class ({@link #replacementClassFqn}). *

- * This quick fix is for locations where it doesn't matter whether the qualifier of the reference should remain untouched or not. + * This quick fix is for locations where it doesn't matter if the qualifier of the reference should remain untouched. *

* In any case, the reference is replaced with the short name of the predefined fully qualified name. + * + * @see NameCollisionAwareReferenceReplacerQuickFix */ -@RequiredArgsConstructor -public class NameCollisionlessReferenceReplacerQuickFix extends MigrationAidV4BaseQuickFix { +public final class NameCollisionlessReferenceReplacerQuickFix extends ReferenceReplacerQuickFix { - private final String nameMessageKey; - private final String replacementClassFqn; - - @Override - public @IntentionName @NotNull String getName() { - return MockitoolsBundle.message(nameMessageKey); + public NameCollisionlessReferenceReplacerQuickFix(String nameMessageKey, String replacementClassFqn) { + super(nameMessageKey, replacementClassFqn); } @Override protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor, PsiElement problemElement) { - Optional.ofNullable(ModuleUtilCore.findModuleForFile(problemElement.getContainingFile().getVirtualFile(), project)) - .map(module -> getElementFactory(project).createReferenceElementByFQClassName(replacementClassFqn, moduleWithLibrariesScope(module))) - .ifPresent(problemElement::replace); + var module = ModuleUtilCore.findModuleForFile(problemElement.getContainingFile().getVirtualFile(), project); + if (module == null) return; + + var refElement = createRefElementByFQN(project, module); + if (refElement != null) problemElement.replace(refElement); } } diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ParameterizedIsNotNullMatcherInspection.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ParameterizedIsNotNullMatcherInspection.java index 182a5edd..fb57b7ab 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ParameterizedIsNotNullMatcherInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ParameterizedIsNotNullMatcherInspection.java @@ -15,11 +15,11 @@ import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.codeInspection.util.IntentionName; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethodCallExpression; import com.siyeh.ig.callMatcher.CallMatcher; +import lombok.Getter; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; @@ -49,19 +49,16 @@ protected void checkMethodCallExpression(PsiMethodCallExpression expression, @No : MockitoolsBundle.message("quick.fix.migration.aid.v4.use.non.parameterized.null.matcher"); holder.registerProblem( getReferenceNameElement(expression), //referenceName null value is already checked by the CallMatcher - MockitoolsBundle.message("inspection.migration.aid.v4.use.non.parameterized.null.matcher", methodName), + MockitoolsBundle.message("inspection.migration.aid.v4.use.non.parameterized.null.matcher"), new ReplaceNullMatcherWithNonParameterizedVariantQuickfix(quickFixName)); } } @RequiredArgsConstructor - private static final class ReplaceNullMatcherWithNonParameterizedVariantQuickfix extends MigrationAidV4BaseQuickFix { - private final String quickFixName; - - @Override - public @IntentionName @NotNull String getName() { - return quickFixName; - } + @Getter + private static final class ReplaceNullMatcherWithNonParameterizedVariantQuickfix extends MigrationAidV4QuickFix { + @NotNull + private final String name; @Override protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor, PsiElement problemElement) { diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/QualifierReplacements.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/QualifierReplacements.java new file mode 100644 index 00000000..a26eca98 --- /dev/null +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/QualifierReplacements.java @@ -0,0 +1,56 @@ +//Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package com.picimako.mockitools.inspection.migrationaids.v4; + +import static com.intellij.psi.search.GlobalSearchScope.moduleWithLibrariesScope; +import static com.picimako.mockitools.MockitoQualifiedNames.ARGUMENT_MATCHERS; +import static com.picimako.mockitools.MockitoQualifiedNames.MATCHERS; +import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_ARGUMENT_MATCHERS; +import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MATCHERS; +import static com.siyeh.ig.psiutils.ImportUtils.nameCanBeImported; + +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementFactory; +import com.intellij.psi.PsiExpression; +import com.siyeh.ig.psiutils.ImportUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * Provides common replacements for expression qualifiers. + */ +final class QualifierReplacements { + + /** + * Replaces {@code org.mockito.Matchers} qualifiers with {@code org.mockito.ArgumentMatchers}. + * + * @param qualifier the qualifier expression to replace. 'org.mockito.Matchers' or 'Matchers' + */ + static void replaceMatchersWithArgumentMatchers(PsiExpression qualifier, @NotNull Project project) { + //if the matcher is referenced with fully qualified name: replace the fqn appropriately + //This also respects that the class is referenced by its fqn rather via import by keeping the fqn of ArgumentMatchers. + if (qualifier.textMatches(ORG_MOCKITO_MATCHERS)) { + replaceQualifierWith(ORG_MOCKITO_ARGUMENT_MATCHERS, qualifier); + } + //if the matcher is referenced as Matchers.: + // then replace the classname (the qualifier) with ArgumentMatchers, + // add import statement for org.mockito.ArgumentMatchers if necessary + else if (qualifier.textMatches(MATCHERS)) { + PsiElement elementAfterReplace = replaceQualifierWith(ARGUMENT_MATCHERS, qualifier); + if (nameCanBeImported(ORG_MOCKITO_ARGUMENT_MATCHERS, elementAfterReplace)) { + //Module scope is used because different modules may use different versions of Mockito + Optional.ofNullable(ModuleUtilCore.findModuleForFile(elementAfterReplace.getContainingFile().getVirtualFile(), project)) + .map(module -> JavaPsiFacade.getInstance(project).findClass(ORG_MOCKITO_ARGUMENT_MATCHERS, moduleWithLibrariesScope(module))) + .ifPresent(argumentMatchers -> ImportUtils.addImportIfNeeded(argumentMatchers, elementAfterReplace)); + } + } + } + + private static PsiElement replaceQualifierWith(String replacement, PsiExpression qualifier) { + return qualifier.replace(PsiElementFactory.getInstance(qualifier.getProject()).createExpressionFromText(replacement, qualifier)); + } +} diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ReferenceReplacerQuickFix.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ReferenceReplacerQuickFix.java new file mode 100644 index 00000000..3c5c0262 --- /dev/null +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/ReferenceReplacerQuickFix.java @@ -0,0 +1,41 @@ +//Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package com.picimako.mockitools.inspection.migrationaids.v4; + +import static com.intellij.psi.JavaPsiFacade.getElementFactory; +import static com.intellij.psi.search.GlobalSearchScope.moduleWithLibrariesScope; + +import com.intellij.codeInspection.util.IntentionName; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.picimako.mockitools.resources.MockitoolsBundle; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.PropertyKey; + +/** + * Base class for quick fixes that perform reference replacements. + */ +@RequiredArgsConstructor +public abstract class ReferenceReplacerQuickFix extends MigrationAidV4QuickFix { + + @PropertyKey(resourceBundle = "messages.MockitoolsBundle") + protected final String nameMessageKey; + protected final String replacementClassFqn; + + @Override + public @IntentionName @NotNull String getName() { + return MockitoolsBundle.message(nameMessageKey); + } + + protected PsiJavaCodeReferenceElement createRefElementByFQN(Project project, Module module) { + return getElementFactory(project) + .createReferenceElementByFQClassName(replacementClassFqn, moduleWithLibrariesScope(module)); + } + + protected PsiJavaCodeReferenceElement createClassNameRefElement(Project project, Module module) { + return getElementFactory(project) + .createFQClassNameReferenceElement(replacementClassFqn, moduleWithLibrariesScope(module)); + } +} diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfAnyMatchersInspection.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfAnyMatchersInspection.java index 61f14c33..cf3ed224 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfAnyMatchersInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfAnyMatchersInspection.java @@ -67,7 +67,7 @@ protected void checkMethodCallExpression(PsiMethodCallExpression expression, @No } } - private static final class ReplaceAnyObjectOrAnyVarargWithAnyQuickFix extends MigrationAidV4BaseQuickFix { + private static final class ReplaceAnyObjectOrAnyVarargWithAnyQuickFix extends MigrationAidV4QuickFix { @Override public @IntentionName @NotNull String getName() { return MockitoolsBundle.message("quick.fix.migration.aid.v4.replace.with", ANY); @@ -83,7 +83,7 @@ protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descri } } - private static final class ReplaceAnyXOfWithAnyXQuickFix extends MigrationAidV4BaseQuickFix { + private static final class ReplaceAnyXOfWithAnyXQuickFix extends MigrationAidV4QuickFix { private static final Pattern ANY_X_OF_PATTERN = Pattern.compile("(?any(Collection|Iterable|List|Map|Set))Of"); private final String anyX; diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedPluginClassesInspection.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedPluginClassesInspection.java index f45374e0..44133960 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedPluginClassesInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedPluginClassesInspection.java @@ -6,8 +6,8 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_PLUGINS_ANNOTATION_ENGINE; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_PLUGINS_INSTANTIATOR_PROVIDER; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_PLUGINS_INSTANTIATOR_PROVIDER_2; -import static com.picimako.mockitools.util.ModuleDependencyHelper.isMockitoCore2xOr3xAvailableInModuleOf; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.migrationaids.v4.ModuleDependencyHelper.isMockitoCore2xOr3xAvailableInModuleOf; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; @@ -19,7 +19,8 @@ import com.picimako.mockitools.resources.MockitoolsBundle; /** - * Reports usages of {@code org.mockito.configuration.AnnotationEngine} and {@code org.mockito.plugins.InstantiatorProvider}, + * Reports usages of {@code org.mockito.configuration.AnnotationEngine} (with {@code org.mockito.plugins.AnnotationEngine}) + * and {@code org.mockito.plugins.InstantiatorProvider} (with {@code org.mockito.plugins.InstantiatorProvider2}), * since they are removed in Mockito v4. *

* Related quick fixes can replace these references with the proper classes. diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedVerifyInspection.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedVerifyInspection.java index 161cc7b2..9b1d7b29 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedVerifyInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfDeprecatedVerifyInspection.java @@ -27,7 +27,7 @@ import com.picimako.mockitools.resources.MockitoolsBundle; /** - * Reports calls to various verify methods which are deprecated, and removed in v4. + * Reports calls to various verify methods which are deprecated and removed in v4. *

* Reported calls: *

    @@ -62,7 +62,7 @@ protected void checkMethodCallExpression(PsiMethodCallExpression expression, @No } } - private static final class SwitchMockedStaticVerifyArgumentsQuickFix extends MigrationAidV4BaseQuickFix { + private static final class SwitchMockedStaticVerifyArgumentsQuickFix extends MigrationAidV4QuickFix { @Override public @IntentionName @NotNull String getName() { @@ -81,7 +81,7 @@ protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descri } } - private static final class ReplaceZeroInteractionsWithNoMoreInteractionsQuickFix extends MigrationAidV4BaseQuickFix { + private static final class ReplaceZeroInteractionsWithNoMoreInteractionsQuickFix extends MigrationAidV4QuickFix { @Override public @IntentionName @NotNull String getName() { diff --git a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfOldJUnitRunnerInspection.java b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfOldJUnitRunnerInspection.java index 2712d1ca..e15fbd09 100644 --- a/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfOldJUnitRunnerInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/migrationaids/v4/UsageOfOldJUnitRunnerInspection.java @@ -6,8 +6,8 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_RUNNERS_CONSOLE_SPAMMING_MOCKITO_JUNIT_RUNNER; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_RUNNERS_MOCKITO_JUNIT_RUNNER; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_RUNNERS_VERBOSE_MOCKITO_JUNIT_RUNNER; -import static com.picimako.mockitools.util.ModuleDependencyHelper.isMockitoCore2xOr3xAvailableInModuleOf; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.migrationaids.v4.ModuleDependencyHelper.isMockitoCore2xOr3xAvailableInModuleOf; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; @@ -19,7 +19,7 @@ import com.picimako.mockitools.resources.MockitoolsBundle; /** - * Reports usages of JUnit runners used from the {@code org.mockito.runners} package, which is removed Mockito v4. + * Reports usages of JUnit runners used from the {@code org.mockito.runners} package, which is removed in Mockito v4. *

    * The applicable quick fixes replace them with {@code org.mockito.junit.MockitoJUnitRunner}. * diff --git a/src/main/java/com/picimako/mockitools/inspection/mocking/GenericInferredMockAndSpyCreationInspection.java b/src/main/java/com/picimako/mockitools/inspection/mocking/GenericInferredMockAndSpyCreationInspection.java index 32245b0d..8442cb19 100644 --- a/src/main/java/com/picimako/mockitools/inspection/mocking/GenericInferredMockAndSpyCreationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/mocking/GenericInferredMockAndSpyCreationInspection.java @@ -12,20 +12,21 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Range; /** - * This inspection reports arguments passed into {@code Mockito.mock()} and {@code Mockito.spy()} which are designed to determine the mock type based on - * the variable's type they are assigned to, and not by the type passed into them. + * This inspection reports arguments passed into {@code Mockito.mock()} and {@code Mockito.spy()} + * which are designed to determine the mock type based on the variable's type they are assigned to, + * and not by the type passed into them. * - * @see Mocking/Spying without specifying class + * @see Mocking/Spying without specifying class * @see com.picimako.mockitools.inspection.captor.GenericInferredCaptorCreationInspection * @since 0.11.0 */ -final class GenericInferredMockAndSpyCreationInspection extends MockitoolsBaseInspection { +final class GenericInferredMockAndSpyCreationInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/inspection/mocking/MockFieldInitializationInspection.java b/src/main/java/com/picimako/mockitools/inspection/mocking/MockFieldInitializationInspection.java index c1750bce..d6c53717 100644 --- a/src/main/java/com/picimako/mockitools/inspection/mocking/MockFieldInitializationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/mocking/MockFieldInitializationInspection.java @@ -4,7 +4,7 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INJECT_MOCKS; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemDescriptor; @@ -15,25 +15,26 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiField; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.QuickFixBase; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; /** - * Since {@code @Mock} and {@code InjectMocks} annotated fields are initialized automagically by Mockito via {@code MockitoJUnitRunner}, {@code MockitoJUnit.rule()} or - * {@code MockitoAnnotations.initMocks()} / {@code MockitoAnnotations.openMocks()}, there is no need for explicit initialization of them. + * Since {@code @Mock} and {@code InjectMocks} annotated fields are initialized automagically by Mockito via + * {@code MockitoJUnitRunner}, {@code MockitoJUnit.rule()} or {@code MockitoAnnotations.initMocks()} / {@code MockitoAnnotations.openMocks()}, + * there is no need for explicit initialization of them. *

    * Explicit initializer is reported only when the field is annotated as either {@code @Mock} or {@code InjectMocks}. *

    * It also provides a quick fix to remove the field initializer. * - * @see @Mock annotation - * @see @InjectMocks annotation + * @see @Mock annotation + * @see @InjectMocks annotation * @see Baeldung - Mockito Annotations * @since 1.3.0 */ -final class MockFieldInitializationInspection extends MockitoolsBaseInspection { +final class MockFieldInitializationInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { @@ -48,7 +49,7 @@ protected void checkField(PsiField field, @NotNull ProblemsHolder holder) { } } - private static final class RemoveFieldInitQuickFix extends BaseQuickFix { + private static final class RemoveFieldInitQuickFix extends QuickFixBase { @Override public @IntentionName @NotNull String getName() { diff --git a/src/main/java/com/picimako/mockitools/inspection/mocking/MockSpiedInstanceTypeMismatchInspection.java b/src/main/java/com/picimako/mockitools/inspection/mocking/MockSpiedInstanceTypeMismatchInspection.java index 5fcd376b..b0149f9c 100644 --- a/src/main/java/com/picimako/mockitools/inspection/mocking/MockSpiedInstanceTypeMismatchInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/mocking/MockSpiedInstanceTypeMismatchInspection.java @@ -5,7 +5,7 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.*; import com.intellij.psi.impl.source.PsiClassReferenceType; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,7 +29,7 @@ * * @since 0.11.0 */ -final class MockSpiedInstanceTypeMismatchInspection extends MockitoolsBaseInspection { +final class MockSpiedInstanceTypeMismatchInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/inspection/mocking/MockTypeInspection.java b/src/main/java/com/picimako/mockitools/inspection/mocking/MockTypeInspection.java index cb833749..0c3087f0 100644 --- a/src/main/java/com/picimako/mockitools/inspection/mocking/MockTypeInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/mocking/MockTypeInspection.java @@ -9,10 +9,10 @@ import static com.picimako.mockitools.MockitoolsPsiUtil.isMockitoMock; import static com.picimako.mockitools.MockitoolsPsiUtil.isMockitoSpy; import static com.picimako.mockitools.resources.MockitoolsBundle.message; -import static com.picimako.mockitools.util.EvaluationHelper.evaluateClassObjectOrNewExpressionType; +import static com.picimako.mockitools.inspection.EvaluationHelper.evaluateClassObjectOrNewExpressionType; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; import static com.picimako.mockitools.util.PsiMethodUtil.hasArgument; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; @@ -21,21 +21,21 @@ import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiType; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import org.jetbrains.annotations.NotNull; /** * Reports {@code @Mock} and {@code @Spy} annotated fields' types and the types specified as the arguments of - * {@code Mockito.mock()} and {@code Mockito.spy()} calls, since certain types are not mockable by Mockito, or are annotated as - * {@code @DoNotMock} be it Mockito's own such annotation or a custom one. + * {@code Mockito.mock()} and {@code Mockito.spy()} calls, since certain types are not mockable by Mockito, + * or are annotated as {@code @DoNotMock} be it Mockito's own such annotation or a custom one. *

    * You can find the logic and the types in the linked resources. *

    - * {@code @Mock} and {@code @Spy} annotated fields are validated only in actual unit test classes, while Mockito calls are validated in any file - * within test sources. + * {@code @Mock} and {@code @Spy} annotated fields are validated only in actual unit test classes, + * while Mockito calls are validated in any file within test sources. *

    - * Types' class hierarchy is inspected and the first type annotated with {@code @DoNotMock} is returned, and used for constructing the inspection message - * including the annotation's reason in it, if there is any. + * Types' class hierarchy is inspected and the first type annotated with {@code @DoNotMock} is returned, + * and used for constructing the inspection message including the annotation's reason in it, if there is any. * * @see InlineDelegateByteBuddyMockMaker#isTypeMockable(Class) * @see InlineBytecodeGenerator#EXCLUDES @@ -44,7 +44,7 @@ * @see Mockito: Add annotation to mark a type as DoNotMock * @since 0.1.0 */ -final class MockTypeInspection extends MockitoolsBaseInspection { +final class MockTypeInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { diff --git a/src/main/java/com/picimako/mockitools/inspection/mocking/SimplifyMockCreationInspection.java b/src/main/java/com/picimako/mockitools/inspection/mocking/SimplifyMockCreationInspection.java index f353b8ee..47657ff9 100644 --- a/src/main/java/com/picimako/mockitools/inspection/mocking/SimplifyMockCreationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/mocking/SimplifyMockCreationInspection.java @@ -21,8 +21,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.QuickFixBase; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -35,7 +35,7 @@ * @see com.picimako.mockitools.intention.mocking.ExpandMockCreationIntention * @since 0.11.0 */ -final class SimplifyMockCreationInspection extends MockitoolsBaseInspection { +final class SimplifyMockCreationInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { @@ -68,7 +68,7 @@ private void registerProblem(PsiElement element, @NotNull ProblemsHolder holder, new ReplaceWithSimplerMockCreationQuickFix()); } - private static final class ReplaceWithSimplerMockCreationQuickFix extends BaseQuickFix { + private static final class ReplaceWithSimplerMockCreationQuickFix extends QuickFixBase { @Override public @IntentionFamilyName @NotNull String getFamilyName() { @@ -83,23 +83,26 @@ protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descri if (SPIED_INSTANCE.equals(settingsMethodName)) { //org.mockito.Mockito.spy() - String mockitoSpy = "org.mockito.Mockito.spy(" + getFirstArgument(settingsMethodCall).getText() + ")"; + String mockitoSpy = "org.mockito.Mockito.spy(" + textOfFirstArg(settingsMethodCall) + ")"; simplify(mockitoMock, mockitoSpy, project); } else if (NAME.equals(settingsMethodName) || DEFAULT_ANSWER.equals(settingsMethodName)) { //org.mockito.Mockito.mock(.class, ) //org.mockito.Mockito.mock(.class, ) - String newMockitoMock = "org.mockito.Mockito.mock(" + getFirstArgument(mockitoMock).getText() + ", " + getFirstArgument(settingsMethodCall).getText() + ")"; + String newMockitoMock = "org.mockito.Mockito.mock(" + textOfFirstArg(mockitoMock) + ", " + textOfFirstArg(settingsMethodCall) + ")"; simplify(mockitoMock, newMockitoMock, project); } } } + private String textOfFirstArg(PsiMethodCallExpression call) { + return getFirstArgument(call).getText(); + } + /** * Replaces the {@code mockitoMock} call with a new method call created from {@code replacementText}. * * @param mockitoMock the {@code Mockito.mock()} call to simplify * @param replacementText the text of the simplified replacement call - * @param project the current project */ private void simplify(PsiMethodCallExpression mockitoMock, String replacementText, Project project) { var simplifiedReplacementCall = JavaCodeStyleManager.getInstance(project) diff --git a/src/main/java/com/picimako/mockitools/inspection/mocking/SpyOnMockInspection.java b/src/main/java/com/picimako/mockitools/inspection/mocking/SpyOnMockInspection.java index 080fc589..1f471176 100644 --- a/src/main/java/com/picimako/mockitools/inspection/mocking/SpyOnMockInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/mocking/SpyOnMockInspection.java @@ -15,7 +15,7 @@ import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiReferenceExpression; import com.picimako.mockitools.dsl.MockObject; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; @@ -28,7 +28,7 @@ * * @since 0.11.0 */ -final class SpyOnMockInspection extends MockitoolsBaseInspection { +final class SpyOnMockInspection extends MockitoolsInspectionBase { private static final CallMatcher MOCKITO_SPY = staticCall(ORG_MOCKITO_MOCKITO, SPY).parameterTypes("T"); diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/CannotStubMethodInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/CannotStubMethodInspection.java index aa7dfa32..0add9292 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/CannotStubMethodInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/CannotStubMethodInspection.java @@ -10,8 +10,8 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiModifier; -import com.picimako.mockitools.StubbingApproach; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.dsl.StubbingApproach; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.util.PsiMethodUtil; import com.siyeh.ig.psiutils.MethodUtils; import org.jetbrains.annotations.NotNull; @@ -25,7 +25,7 @@ * @see Add native method to exception message * @since 1.3.0 */ -final class CannotStubMethodInspection extends MockitoolsBaseInspection { +final class CannotStubMethodInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspection.java index e7a4f262..21166673 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspection.java @@ -2,10 +2,9 @@ package com.picimako.mockitools.inspection.stubbing; +import static com.picimako.mockitools.MockitoMockMatchers.IN_ORDER_VERIFY_NON_MOCKED_STATIC; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; -import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE; -import static com.picimako.mockitools.MockitoolsPsiUtil.INORDER_VERIFY; import static com.picimako.mockitools.util.PsiMethodUtil.getReferenceNameElement; import static com.siyeh.ig.callMatcher.CallMatcher.staticCall; @@ -13,8 +12,8 @@ import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.util.ui.JBUI; -import com.picimako.mockitools.Convention; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.dsl.Convention; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationIntentionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import com.siyeh.ig.callMatcher.CallMatcher; @@ -42,11 +41,7 @@ * @since 0.4.0 */ @SuppressWarnings("ExtensionClassShouldBeFinalAndNonPublic") -public final class EnforceConventionInspection extends MockitoolsBaseInspection { - //MockedStatic specific verify() methods are excluded, since BDDMockito has no way to verify MockedStatic - public static final CallMatcher IN_ORDER_VERIFY_NON_MOCKED_STATIC = CallMatcher.anyOf( - INORDER_VERIFY.parameterTypes("T"), - INORDER_VERIFY.parameterTypes("T", ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE)); +public final class EnforceConventionInspection extends MockitoolsInspectionBase { private static final CallMatcher MOCKITO_MATCHER = CallMatcher.anyOf( staticCall(ORG_MOCKITO_MOCKITO, "when", "doReturn", "doThrow", "doAnswer", "doCallRealMethod", "doNothing", //stubbing @@ -58,7 +53,6 @@ public final class EnforceConventionInspection extends MockitoolsBaseInspection "then" //verification ); - @SuppressWarnings("PublicField") public Convention conventionToEnforce = Convention.MOCKITO; @Override diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/ExceptionStubber.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/ExceptionStubber.java index 50447e55..7013d686 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/ExceptionStubber.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/ExceptionStubber.java @@ -26,7 +26,13 @@ public final class ExceptionStubber { * The method name that accepts the mock object or the call on a mock object. Usually {@code given} or {@code when}. */ private final CallMatcher matcher; + /** + * Call matcher for the {@code Class} specific override of {@code *Throw()} calls. + */ public final CallMatcher classMatcher; + /** + * Call matcher for the {@code Throwable} specific override of {@code *Throw()} calls. + */ public final CallMatcher throwablesMatcher; public ExceptionStubber(String methodName, String instanceClassName, @Nullable String staticClassName) { diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspection.java index 5d438770..73217756 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspection.java @@ -9,7 +9,7 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK; import static com.picimako.mockitools.MockitoolsPsiUtil.isExtraInterfaces; import static com.picimako.mockitools.util.PsiMethodUtil.getArguments; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; @@ -18,7 +18,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.PropertyKey; @@ -41,7 +41,7 @@ * @see Report.java: look for extraInterfacesRequiresAtLeastOneInterface() * @since 0.1.0 */ -final class ExtraInterfacesInspection extends MockitoolsBaseInspection { +final class ExtraInterfacesInspection extends MockitoolsInspectionBase { @Override public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspection.java index f871da77..92007505 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspection.java @@ -5,9 +5,9 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_ADDITIONAL_MATCHERS; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_ARGUMENT_MATCHERS; import static com.picimako.mockitools.MockitoolsPsiUtil.isAdditionalMatchers; -import static com.picimako.mockitools.StubbingApproach.BDDMOCKITO_GIVEN; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_DO_X; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_WHEN; +import static com.picimako.mockitools.dsl.StubbingApproach.BDDMOCKITO_GIVEN; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_DO_X; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_WHEN; import static com.siyeh.ig.callMatcher.CallMatcher.staticCall; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; @@ -16,9 +16,8 @@ import com.intellij.psi.PsiExpressionList; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiTypeCastExpression; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; -import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -37,12 +36,12 @@ * TODO: Additional customization option may be added in IDE settings to configure classes that also provide static methods for creating argument matchers, * besides ArgumentMatchers and AdditionalMatchers. * - * @see Argument matchers documentation + * @see Argument matchers documentation * @see ArgumentMatchers javadoc * @see SonarLint alternative * @since 0.1.0 */ -final class InconsistentArgumentMatcherUsageInspection extends MockitoolsBaseInspection { +final class InconsistentArgumentMatcherUsageInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { @@ -90,9 +89,7 @@ else if (arg instanceof PsiTypeCastExpression typeCast && typeCast.getOperand() } private boolean isArgumentMatcher(PsiExpression arg, String methodName) { - return CallMatcher.anyOf( - staticCall(ORG_MOCKITO_ARGUMENT_MATCHERS, methodName), - staticCall(ORG_MOCKITO_ADDITIONAL_MATCHERS, methodName) - ).matches(arg); + return staticCall(ORG_MOCKITO_ARGUMENT_MATCHERS, methodName).matches(arg) + || staticCall(ORG_MOCKITO_ADDITIONAL_MATCHERS, methodName).matches(arg); } } diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspection.java index e74127e2..796e1eaa 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspection.java @@ -3,38 +3,37 @@ package com.picimako.mockitools.inspection.stubbing; import static com.picimako.mockitools.MockitoQualifiedNames.IN_ORDER; +import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.VERIFY_NO_INTERACTIONS; import static com.picimako.mockitools.MockitoQualifiedNames.VERIFY_NO_MORE_INTERACTIONS; import static com.picimako.mockitools.MockitoQualifiedNames.VERIFY_ZERO_INTERACTIONS; -import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import static com.picimako.mockitools.MockitoolsPsiUtil.isExtraInterfaces; import static com.picimako.mockitools.MockitoolsPsiUtil.isIgnoreStubs; +import static com.picimako.mockitools.resources.MockitoolsBundle.message; import static com.picimako.mockitools.util.PsiMethodUtil.getReferenceNameElement; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; -import java.util.Optional; - import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; -import com.picimako.mockitools.resources.MockitoolsBundle; +import java.util.Optional; /** - * Reports method calls that must have at least one argument passed but none is provided. + * Reports method calls that must have at least one argument passed in, but none is provided. *

    * These are usually methods with a vararg as their only parameter. * * @since 0.1.0 */ -final class NoMethodCallArgumentSpecifiedInspection extends MockitoolsBaseInspection { +final class NoMethodCallArgumentSpecifiedInspection extends MockitoolsInspectionBase { private static final CallMatcher VERIFY_CALLS = - CallMatcher.staticCall(ORG_MOCKITO_MOCKITO, VERIFY_NO_INTERACTIONS, VERIFY_NO_MORE_INTERACTIONS, VERIFY_ZERO_INTERACTIONS, IN_ORDER); + CallMatcher.staticCall(ORG_MOCKITO_MOCKITO, VERIFY_NO_INTERACTIONS, VERIFY_NO_MORE_INTERACTIONS, VERIFY_ZERO_INTERACTIONS /*Mockito v3*/, IN_ORDER); @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { @@ -49,6 +48,6 @@ protected void checkMethodCallExpression(PsiMethodCallExpression expression, @No private void registerProblem(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder, ProblemHighlightType problemHighlightType) { PsiElement elementToHighlight = Optional.ofNullable(getReferenceNameElement(expression)).orElseGet(expression::getMethodExpression); - holder.registerProblem(elementToHighlight, MockitoolsBundle.message("inspection.method.call.no.argument.specified", getMethodName(expression)), problemHighlightType); + holder.registerProblem(elementToHighlight, message("inspection.method.call.no.argument.specified", getMethodName(expression)), problemHighlightType); } } diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspection.java index 431d3aee..4dcd3592 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspection.java @@ -3,8 +3,9 @@ package com.picimako.mockitools.inspection.stubbing; import static com.intellij.util.containers.ContainerUtil.filter; -import static com.picimako.mockitools.StubbingApproach.BDDMOCKITO_WILL_X; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_DO_X; +import static com.picimako.mockitools.dsl.StubbingApproach.BDDMOCKITO_WILL_X; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_DO_X; +import static com.picimako.mockitools.resources.MockitoolsBundle.message; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.getReferenceNameElement; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; @@ -15,30 +16,31 @@ import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiType; import com.intellij.psi.PsiTypes; -import com.picimako.mockitools.StubbingApproach; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; -import com.picimako.mockitools.resources.MockitoolsBundle; +import com.picimako.mockitools.dsl.StubbingApproach; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import org.jetbrains.annotations.NotNull; import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; +import java.util.function.Supplier; /** * Reports {@code doNothing()} and {@code willDoNothing()} calls when the stubbed method's return type is not void. *

    - * Based on Mockito's corresponding error handling, + * Based on Mockito's + * corresponding error handling, * Only void methods can doNothing(). *

    * Also reports {@code *Return()} calls when the stubbed method's return type is void, - * which is based on Mockito's corresponding error handling, + * which is based on Mockito's + * corresponding error handling, *

    * It highlights every instance of {@code doNothing()}, {@code willDoNothing()} and {@code *Return()} calls in the affected call chains. * * @since 0.7.0 */ -final class StubbingAndMethodReturnTypeMismatchInspection extends MockitoolsBaseInspection { +final class StubbingAndMethodReturnTypeMismatchInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { @@ -53,13 +55,9 @@ private void checkAndRegister(@NotNull String doNothingMethod, @NotNull String d PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { var calls = collectCallsInChainFromFirst(expression, true); if (approach.isValid(calls)) { - var doNothingMethodCalls = findMethodCalls(doNothingMethod, calls); - if (!doNothingMethodCalls.isEmpty()) - registerIfStubbedMethodsReturnTypeIsNotVoid(approach.getStubbedMethodCall(calls), doNothingMethodCalls, holder); - - var doReturnMethodCalls = findMethodCalls(doReturnMethod, calls); - if (!doReturnMethodCalls.isEmpty()) - registerIfStubbedMethodsReturnTypeIsVoid(approach.getStubbedMethodCall(calls), doReturnMethodCalls, holder); + var stubbedMethodCall = approach.getStubbedMethodCall(calls); + registerIfStubbedMethodsReturnTypeIsNotVoid(stubbedMethodCall, () -> findMethodCalls(doNothingMethod, calls), holder); + registerIfStubbedMethodsReturnTypeIsVoid(stubbedMethodCall, () -> findMethodCalls(doReturnMethod, calls), holder); } } @@ -69,8 +67,8 @@ private void checkAndRegister(@NotNull String doNothingMethod, @NotNull String d * @param methodName {@code doNothing} or {@code willDoNothing}, or {@code doReturn}, {@code thenReturn} or {@code willReturn} * @param calls the stubbing call chain */ - private static Set findMethodCalls(@NotNull String methodName, List calls) { - return new HashSet<>(filter(calls, call -> methodName.equals(getMethodName(call)))); + private static List findMethodCalls(@NotNull String methodName, List calls) { + return filter(calls, call -> methodName.equals(getMethodName(call))); } /** @@ -78,16 +76,17 @@ private static Set findMethodCalls(@NotNull String meth * * @param stubbedMethod the stubbed method called sequent to {@code given(mock)} or {@code when(mock)} */ + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private void registerIfStubbedMethodsReturnTypeIsNotVoid(Optional stubbedMethod, - Set doNothingMethodCalls, + @NotNull Supplier> doNothingMethodCalls, @NotNull ProblemsHolder holder) { stubbedMethod .flatMap(this::getReturnTypeOf) .filter(type -> !type.equals(PsiTypes.voidType())) .ifPresent(__ -> { - for (var doNothing : doNothingMethodCalls) { + for (var doNothing : new HashSet<>(doNothingMethodCalls.get())) { holder.registerProblem(getReferenceNameElement(doNothing), - MockitoolsBundle.message("inspection.void.method.is.stubbed.to.do.nothing", getMethodName(stubbedMethod.get()))); + message("inspection.void.method.is.stubbed.to.do.nothing", getMethodName(stubbedMethod.get()))); } }); } @@ -97,16 +96,17 @@ private void registerIfStubbedMethodsReturnTypeIsNotVoid(Optional stubbedMethod, - Set doReturnMethodCalls, + @NotNull Supplier> doReturnMethodCalls, @NotNull ProblemsHolder holder) { stubbedMethod .flatMap(this::getReturnTypeOf) .filter(type -> type.equals(PsiTypes.voidType())) .ifPresent(__ -> { - for (var doReturn : doReturnMethodCalls) { + for (var doReturn : new HashSet<>(doReturnMethodCalls.get())) { holder.registerProblem(getReferenceNameElement(doReturn), - MockitoolsBundle.message("inspection.non.void.method.is.stubbed.with.return.value", getMethodName(stubbedMethod.get()))); + message("inspection.non.void.method.is.stubbed.with.return.value", getMethodName(stubbedMethod.get()))); } }); } diff --git a/src/main/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspection.java b/src/main/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspection.java index 982686c8..133d0adc 100644 --- a/src/main/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspection.java @@ -4,10 +4,12 @@ import static com.intellij.psi.CommonClassNames.JAVA_LANG_EXCEPTION; import static com.intellij.psi.CommonClassNames.JAVA_LANG_THROWABLE; -import static com.picimako.mockitools.util.EvaluationHelper.evaluateClassObjectOrNewExpressionType; -import static com.picimako.mockitools.util.EvaluationHelper.evaluateType; -import static com.picimako.mockitools.util.ExceptionUtil.isCheckedException; -import static com.picimako.mockitools.util.PointersUtil.toPointer; +import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; +import static com.intellij.util.containers.ContainerUtil.find; +import static com.picimako.mockitools.inspection.EvaluationHelper.evaluateClassObjectOrNewExpressionType; +import static com.picimako.mockitools.inspection.EvaluationHelper.evaluateType; +import static com.picimako.mockitools.inspection.PointersUtil.toPointer; +import static com.picimako.mockitools.util.MockitoolsContainerUtil.none; import static com.picimako.mockitools.util.PsiMethodUtil.getArguments; import static com.picimako.mockitools.util.PsiMethodUtil.getSubsequentMethodCall; @@ -15,20 +17,27 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.util.IntentionFamilyName; import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import com.picimako.mockitools.StubType; -import com.picimako.mockitools.StubbingApproach; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClassObjectAccessExpression; +import com.intellij.psi.PsiClassType; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiMethodCallExpression; +import com.intellij.psi.PsiType; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.psi.util.InheritanceUtil; +import com.picimako.mockitools.dsl.StubType; +import com.picimako.mockitools.dsl.StubbingApproach; +import com.picimako.mockitools.inspection.QuickFixBase; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; +import com.picimako.mockitools.util.ClassObjectAccessUtil; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Optional; - /** * Reports exception references in {@code *Throw()} stubbing methods based on Mockito's rule on checked exceptions: * If [the specified exception types] contain a checked exception then it has to match one of the checked exceptions of method signature. @@ -47,14 +56,12 @@ * * @since 0.3.0 */ -final class ThrowsCheckedExceptionStubbingInspection extends MockitoolsBaseInspection { +final class ThrowsCheckedExceptionStubbingInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { - Arrays.stream(StubbingApproach.values()) - .filter(approach -> approach.getExceptionStubber().isApplicableTo(expression)) - .findFirst() - .ifPresent(approach -> checkAndRegister(approach, expression, holder)); + var approach = find(StubbingApproach.values(), sa -> sa.getExceptionStubber().isApplicableTo(expression)); + if (approach != null) checkAndRegister(approach, expression, holder); } /** @@ -89,18 +96,36 @@ && doesntHaveExceptionOrThrowableInThrowsClause(exceptionTypesInThrowsClause)) { }); } + /** + * Returns whether the exception class referenced by the argument expression is a checked exception. + */ + private static boolean isCheckedException(PsiExpression expression) { + return expression instanceof PsiClassObjectAccessExpression + ? isCheckedException(ClassObjectAccessUtil.getOperandType(expression)) + : isCheckedException(expression.getType()); + } + + /** + * Returns whether the argument type is a checked exception. + */ + private static boolean isCheckedException(@Nullable PsiType type) { + return !InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION) + && !InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ERROR); + } + private static boolean doesntHaveMatchingCheckedExceptionInThrowsClause(PsiClassType[] exceptionTypesInThrowsClause, PsiExpression stubbedException) { var stubbedExceptionType = evaluateType(stubbedException); - return stubbedExceptionType != null && Arrays.stream(exceptionTypesInThrowsClause).noneMatch(type -> type.getCanonicalText().equals(stubbedExceptionType.getCanonicalText())); + return stubbedExceptionType != null + && none(exceptionTypesInThrowsClause, type -> type.getCanonicalText().equals(stubbedExceptionType.getCanonicalText())); } private static boolean doesntHaveExceptionOrThrowableInThrowsClause(PsiClassType[] exceptionTypesInThrowsClause) { - return Arrays.stream(exceptionTypesInThrowsClause) - .noneMatch(type -> { + return none(exceptionTypesInThrowsClause, type -> { String typeCanonicalText = type.getCanonicalText(); - //Checking java.lang.Exception because it might be more likely to be used in a throws clause than java.lang.Throwable + //Checking java.lang.Exception first because it might be more likely to be used in a 'throws' clause than java.lang.Throwable return typeCanonicalText.equals(JAVA_LANG_EXCEPTION) || typeCanonicalText.equals(JAVA_LANG_THROWABLE); - }); + } + ); } /** @@ -113,11 +138,10 @@ private static boolean doesntHaveExceptionOrThrowableInThrowsClause(PsiClassType */ @Nullable private PsiMethod resolveStubbedMethod(PsiExpression stub, StubType stubType) { - return stubType == StubType.STUBBING - ? ((PsiMethodCallExpression) stub).resolveMethod() - : Optional.ofNullable(getSubsequentMethodCall(PsiTreeUtil.getParentOfType(stub, PsiMethodCallExpression.class))) - .map(PsiCall::resolveMethod) - .orElse(null); + if (stubType == StubType.STUBBING) return ((PsiMethodCallExpression) stub).resolveMethod(); + + var subsequentMethodCall = getSubsequentMethodCall(getParentOfType(stub, PsiMethodCallExpression.class)); + return subsequentMethodCall != null ? subsequentMethodCall.resolveMethod() : null; } /** @@ -147,7 +171,7 @@ private PsiMethod resolveStubbedMethod(PsiExpression stub, StubType stubType) { * @since 0.5.0 */ @RequiredArgsConstructor - private static final class AddExceptionToThrowsClauseQuickFix extends BaseQuickFix { + private static final class AddExceptionToThrowsClauseQuickFix extends QuickFixBase { private final SmartPsiElementPointer stubbedMethod; @Override diff --git a/src/main/java/com/picimako/mockitools/inspection/verification/CannotVerifyToStringInspection.java b/src/main/java/com/picimako/mockitools/inspection/verification/CannotVerifyToStringInspection.java index 2e62c285..769edc88 100644 --- a/src/main/java/com/picimako/mockitools/inspection/verification/CannotVerifyToStringInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/verification/CannotVerifyToStringInspection.java @@ -8,8 +8,8 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.VerificationApproach; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.dsl.VerificationApproach; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -19,10 +19,10 @@ * The inspection doesn't distinguish between {@code toString()} calls that come directly as overridden methods * from the mock objects' classes, or when they come from a type deeper in the class hierarchy. * - * @see Cannot verify toString() + * @see Cannot verify toString() * @since 0.7.0 */ -final class CannotVerifyToStringInspection extends MockitoolsBaseInspection { +final class CannotVerifyToStringInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspection.java b/src/main/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspection.java index 35828860..69d5af08 100644 --- a/src/main/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspection.java @@ -4,7 +4,7 @@ import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INORDER; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import static com.siyeh.ig.psiutils.TypeUtils.typeEquals; import com.intellij.codeInspection.LocalInspectionTool; @@ -16,7 +16,7 @@ import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.search.searches.ReferencesSearch; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -38,14 +38,12 @@ public void visitLocalVariable(@NotNull PsiLocalVariable variable) { var inOrderRefs = ReferencesSearch.search(variable).findAll(); //If there is only one reference to/usage of the InOrder variable - if (inOrderRefs.size() == 1) { - var ref = inOrderRefs.iterator().next(); - if (ref instanceof PsiReferenceExpression inOrderRef) { - var verifyOrShould = getParentOfType(inOrderRef, PsiMethodCallExpression.class); - //If the only usage is a method call to InOrder.verify() or BDDMockito.should(InOrder) - if (VerificationApproach.INORDER_VERIFY.isVerifiedBy(verifyOrShould) || VerificationApproach.BDDMOCKITO_THEN_SHOULD.isInOrderSpecific(verifyOrShould)) - holder.registerProblem(variable.getNameIdentifier(), MockitoolsBundle.message("inspection.in.order.is.used.only.once")); - } + if (inOrderRefs.size() == 1 && inOrderRefs.iterator().next() instanceof PsiReferenceExpression inOrderRef) { + var verifyOrShould = getParentOfType(inOrderRef, PsiMethodCallExpression.class); + //If the only usage is a method call to InOrder.verify() or BDDMockito.should(InOrder) + if (VerificationApproach.INORDER_VERIFY.isVerifiedBy(verifyOrShould) + || VerificationApproach.BDDMOCKITO_THEN_SHOULD.isInOrderSpecific(verifyOrShould)) + holder.registerProblem(variable.getNameIdentifier(), MockitoolsBundle.message("inspection.in.order.is.used.only.once")); } super.visitLocalVariable(variable); diff --git a/src/main/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspection.java b/src/main/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspection.java index fb766c44..74035a1a 100644 --- a/src/main/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspection.java @@ -5,9 +5,9 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiReferenceExpression; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.dsl.MockObject; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -19,7 +19,7 @@ * * @since 0.8.0 */ -final class StubOnlyMockInVerificationInspection extends MockitoolsBaseInspection { +final class StubOnlyMockInVerificationInspection extends MockitoolsInspectionBase { @Override protected void checkMethodCallExpression(PsiMethodCallExpression expression, @NotNull ProblemsHolder holder) { diff --git a/src/main/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspection.java b/src/main/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspection.java index 7c4d6b6b..c2597dba 100644 --- a/src/main/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspection.java @@ -12,7 +12,7 @@ import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromLast; import static com.picimako.mockitools.util.PsiMethodUtil.get2ndArgument; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import static com.siyeh.ig.callMatcher.CallMatcher.instanceCall; import com.intellij.codeInspection.LocalInspectionToolSession; @@ -27,8 +27,8 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiLiteralUtil; import com.picimako.mockitools.MockitoolsPsiUtil; -import com.picimako.mockitools.inspection.BaseQuickFix; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.QuickFixBase; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; @@ -49,23 +49,20 @@ * {@code Mockito.times(1)} calls are reported only when they have no subsequent calls on them, e.g. * {@code Mockito.times(1).description("message")}. * - * @see Mockito exact verification documentation + * @see Mockito exact verification documentation * @since 0.1.0 */ -final class TimesVerificationModeInspection extends MockitoolsBaseInspection { +final class TimesVerificationModeInspection extends MockitoolsInspectionBase { + private static final CallMatcher.Simple SHOULD = instanceCall(ORG_MOCKITO_BDDMOCKITO_THEN, "should"); private static final CallMatcher VERIFICATION = CallMatcher.anyOf( MockitoolsPsiUtil.MOCKITO_VERIFY.parameterTypes("T", ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE), MockitoolsPsiUtil.INORDER_VERIFY.parameterTypes("T", ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE), MockitoolsPsiUtil.MOCKED_STATIC_VERIFY.parameterTypes(ORG_MOCKITO_MOCKED_STATIC_VERIFICATION, ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE), - instanceCall(ORG_MOCKITO_BDDMOCKITO_THEN, "should").parameterTypes(ORG_MOCKITO_INORDER, ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE)); - private static final CallMatcher BDDMOCKITO_SHOULD = - instanceCall(ORG_MOCKITO_BDDMOCKITO_THEN, "should").parameterTypes(ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE); + SHOULD.parameterTypes(ORG_MOCKITO_INORDER, ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE)); + private static final CallMatcher BDDMOCKITO_SHOULD = SHOULD.parameterTypes(ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE); - @SuppressWarnings("PublicField") public boolean reportTimesZeroToNever = true; - - @SuppressWarnings("PublicField") public boolean reportTimesOneCanBeOmitted = true; @Override @@ -152,7 +149,7 @@ protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descri } } - private abstract static class TimesQuickFix extends BaseQuickFix { + private abstract static class TimesQuickFix extends QuickFixBase { @Override public @IntentionFamilyName @NotNull String getFamilyName() { return MockitoolsBundle.message("quick.fix.times.family.name"); diff --git a/src/main/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspection.java b/src/main/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspection.java index 8a713d61..18212942 100644 --- a/src/main/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspection.java @@ -4,11 +4,12 @@ import static com.intellij.psi.util.PsiTreeUtil.findChildOfType; import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; +import static com.intellij.util.containers.ContainerUtil.all; import static com.intellij.util.containers.ContainerUtil.map; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INORDER; +import static com.picimako.mockitools.inspection.UnitTestPsiUtil.isInTestSourceContent; import static com.picimako.mockitools.util.PsiMethodUtil.getArguments; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; -import static com.picimako.mockitools.util.UnitTestPsiUtil.isInTestSourceContent; import static com.siyeh.ig.psiutils.TypeUtils.typeEquals; import com.intellij.codeInspection.LocalInspectionTool; @@ -25,11 +26,10 @@ import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.SmartList; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -38,7 +38,7 @@ *

      *
    • The mock is added to the arguments of {@code Mockito.inOrder()} but is not used in any verification * performed via that {@code InOrder} object.
    • - *
    • The mock is used in an {@code InOrder} verification but it is not added to the arguments of {@code Mockito.inOrder()}.
    • + *
    • The mock is used in an {@code InOrder} verification, but it is not added to the arguments of {@code Mockito.inOrder()}.
    • *
    * * @since 0.5.0 @@ -71,10 +71,10 @@ public void visitLocalVariable(@NotNull PsiLocalVariable variable) { } //The mock arguments as Strings from 'Mockito.inOrder()' - var mocksInMockitoInOrderAsString = Arrays.stream(mocksInMockitoInOrder).map(PsiElement::getText).toList(); + var mocksInMockitoInOrderAsString = map(mocksInMockitoInOrder, PsiElement::getText); //Report all mocks in verifications that are not configured in 'Mockito.inOrder()' //This corresponds to the 'inOrderRequiresFamiliarMock()' method in - // https://github.com/mockito/mockito/blob/main/src/main/java/org/mockito/internal/exceptions/Reporter.java + // https://github.com/mockito/mockito/blob/main/mockito-core/src/main/java/org/mockito/internal/exceptions/Reporter.java for (var mockInVerification : mocksInVerifications) { if (!mocksInMockitoInOrderAsString.contains(mockInVerification.getText())) holder.registerProblem(mockInVerification, MockitoolsBundle.message("inspection.mock.is.not.configured.in.in.order")); @@ -94,7 +94,7 @@ public void visitLocalVariable(@NotNull PsiLocalVariable variable) { * the related mock objects are used. */ private boolean areAllVerifications(PsiReference[] inOrderRefs) { - return Arrays.stream(inOrderRefs).allMatch(ref -> { + return all(inOrderRefs, ref -> { if (ref instanceof PsiReferenceExpression inOrderRef) { var verifyOrShould = getParentOfType(inOrderRef, PsiMethodCallExpression.class); return VerificationApproach.INORDER_VERIFY.isVerifiedBy(verifyOrShould) diff --git a/src/main/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspection.java b/src/main/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspection.java index f3a06a0b..6bcf89d1 100644 --- a/src/main/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspection.java +++ b/src/main/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspection.java @@ -2,7 +2,8 @@ package com.picimako.mockitools.inspection.verification; -import static com.intellij.codeInspection.options.OptPane.*; +import static com.intellij.codeInspection.options.OptPane.number; +import static com.intellij.codeInspection.options.OptPane.pane; import static com.picimako.mockitools.MockitoQualifiedNames.AT_LEAST; import static com.picimako.mockitools.MockitoQualifiedNames.AT_MOST; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; @@ -10,6 +11,7 @@ import static com.picimako.mockitools.MockitoolsPsiUtil.isAfter; import static com.picimako.mockitools.MockitoolsPsiUtil.isCalls; import static com.picimako.mockitools.MockitoolsPsiUtil.isTimeout; +import static com.picimako.mockitools.resources.MockitoolsBundle.message; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; import static com.siyeh.ig.callMatcher.CallMatcher.staticCall; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; @@ -18,12 +20,10 @@ import com.intellij.codeInspection.options.OptPane; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.util.PsiLiteralUtil; -import com.picimako.mockitools.inspection.MockitoolsBaseInspection; +import com.picimako.mockitools.inspection.MockitoolsInspectionBase; import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; -import com.picimako.mockitools.resources.MockitoolsBundle; - /** * Reports when arguments of calls on time and occurrence based {@code VerificationMode}s are out of allowed bounds. *

    @@ -39,23 +39,19 @@ *

    * None of these calls are allowed negative values as argument. Additionally, {@code Mockito.calls()} doesn't allow 0 as argument either, * and {@code Mockito.timeout()} doesn't allow values above a user-defined threshold (with 5000 as its default value.) - *

    - * Only unit test classes (class name ending with Test) are considered, since (in ordinary projects) Mockito is supposed to be used only in test classes. - *

    * * @since 0.1.0 */ -final class VerificationModeValuesBetweenLimitsInspection extends MockitoolsBaseInspection { +final class VerificationModeValuesBetweenLimitsInspection extends MockitoolsInspectionBase { private static final CallMatcher MOCKITO_OCCURRENCE_BASED_VERIFICATION_MODES = staticCall(ORG_MOCKITO_MOCKITO, TIMES, AT_LEAST, AT_MOST).parameterCount(1); - @SuppressWarnings("PublicField") public int timeoutMaxThreshold = 5000; @Override public @NotNull OptPane getOptionsPane() { return pane( - number("timeoutMaxThreshold", MockitoolsBundle.message("inspection.timeout.max.threshold.config.title"), Integer.MIN_VALUE, Integer.MAX_VALUE)); + number("timeoutMaxThreshold", message("inspection.timeout.max.threshold.config.title"), Integer.MIN_VALUE, Integer.MAX_VALUE)); } @Override @@ -76,7 +72,7 @@ private void checkIntegerArgumentValue(int upperLimit, PsiMethodCallExpression m if (argValue != null && argValue < upperLimit) { holder.registerProblem(verificationModeArgument, - MockitoolsBundle.message("inspection.verification.mode.value.less.than.allowed", getMethodName(methodCall), upperLimit)); + message("inspection.verification.mode.value.less.than.allowed", getMethodName(methodCall), upperLimit)); } } @@ -87,10 +83,10 @@ private void checkLongArgumentValue(PsiMethodCallExpression methodCall, boolean if (argValue != null) { if (argValue < 0L /*upper limit*/) { holder.registerProblem(verificationModeArgument, - MockitoolsBundle.message("inspection.verification.mode.value.less.than.allowed", getMethodName(methodCall), 0L)); + message("inspection.verification.mode.value.less.than.allowed", getMethodName(methodCall), 0L)); } if (isTimeout && argValue > timeoutMaxThreshold) { - holder.registerProblem(verificationModeArgument, MockitoolsBundle.message("inspection.timeout.value.more.than.allowed", timeoutMaxThreshold)); + holder.registerProblem(verificationModeArgument, message("inspection.timeout.value.more.than.allowed", timeoutMaxThreshold)); } } } diff --git a/src/main/java/com/picimako/mockitools/intention/ConvertCallToFieldIntentionBase.java b/src/main/java/com/picimako/mockitools/intention/ConvertCallToFieldIntentionBase.java index ac576a94..79f8fe55 100644 --- a/src/main/java/com/picimako/mockitools/intention/ConvertCallToFieldIntentionBase.java +++ b/src/main/java/com/picimako/mockitools/intention/ConvertCallToFieldIntentionBase.java @@ -3,10 +3,12 @@ package com.picimako.mockitools.intention; import static com.intellij.openapi.application.ReadAction.computeBlocking; +import static com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction; import static com.intellij.util.containers.ContainerUtil.exists; +import static com.picimako.mockitools.intention.ListPopupHelper.selectItemAndRun; +import static com.picimako.mockitools.intention.PsiClassUtil.getParentClasses; import static com.picimako.mockitools.util.ClassObjectAccessUtil.resolveOperandType; -import static com.picimako.mockitools.util.ListPopupHelper.selectItemAndRun; -import static com.picimako.mockitools.util.PsiClassUtil.getParentClasses; +import static com.picimako.mockitools.util.MockitoolsContainerUtil.none; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; import static com.picimako.mockitools.util.Ranges.endOffsetOf; import static java.util.stream.Collectors.joining; @@ -17,12 +19,10 @@ import com.intellij.codeInspection.util.IntentionName; import com.intellij.ide.util.PsiClassListCellRenderer; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClassObjectAccessExpression; @@ -87,24 +87,17 @@ public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws return new ConversionContext((PsiMethodCallExpression) element.getParent().getParent(), editor, project); }); - if (ctx.mockTypeOrObject instanceof PsiClassObjectAccessExpression) { - selectTargetClassAndIntroduceField(getParentClasses(ctx.mockTypeOrObject), - project, - editor, - targetClass -> introduceFieldForClassObjectAccess(ctx.targetClass(targetClass))); - } - //This branch is valid only in case of ConvertSpyCallToFieldIntention - else if (ctx.mockTypeOrObject instanceof PsiNewExpression) { - selectTargetClassAndIntroduceField(getParentClasses(ctx.mockTypeOrObject), - project, - editor, - targetClass -> introduceFieldForNewExpression(ctx.targetClass(targetClass))); - } else if (ctx.mockTypeOrObject instanceof PsiLocalVariable) { - selectTargetClassAndIntroduceField(getParentClasses(ctx.mockTypeOrObject), - project, - editor, - targetClass -> introduceFieldForLocalVariableType(ctx.targetClass(targetClass))); - } + @Nullable Consumer introduceField = switch (ctx.mockTypeOrObject) { + case PsiClassObjectAccessExpression __ -> + targetClass -> introduceFieldForClassObjectAccess(ctx.targetClass(targetClass)); + //This branch is valid only in case of ConvertSpyCallToFieldIntention + case PsiNewExpression __ -> targetClass -> introduceFieldForNewExpression(ctx.targetClass(targetClass)); + case PsiLocalVariable __ -> targetClass -> introduceFieldForLocalVariableType(ctx.targetClass(targetClass)); + case null, default -> null; + }; + + if (introduceField != null) + selectTargetClassAndIntroduceField(getParentClasses(ctx.mockTypeOrObject), project, editor, introduceField); } /** @@ -123,7 +116,7 @@ protected void introduceFieldForNewExpression(ConversionContext ctx) { */ protected void introduceFieldForClassObjectAccess(ConversionContext ctx) { introduceField(ctx, - () -> Couple.of(resolveOperandType(ctx.mockTypeOrObject).getName(), toLowerCamel(resolveOperandType(ctx.mockTypeOrObject).getName())), + () -> new FieldTypeAndName(resolveOperandType(ctx.mockTypeOrObject).getName(), toLowerCamel(resolveOperandType(ctx.mockTypeOrObject).getName())), NO_INITIALIZER); } @@ -133,7 +126,7 @@ protected void introduceFieldForClassObjectAccess(ConversionContext ctx) { */ protected void introduceFieldForLocalVariableType(ConversionContext ctx) { var localVar = (PsiLocalVariable) ctx.mockTypeOrObject; - introduceField(ctx, () -> Couple.of(localVar.getType().getPresentableText(), localVar.getName()), NO_INITIALIZER); + introduceField(ctx, () -> new FieldTypeAndName(localVar.getType().getPresentableText(), localVar.getName()), NO_INITIALIZER); } /** @@ -147,13 +140,13 @@ private void selectTargetClassAndIntroduceField(List parentClasses, Pr } } - protected void introduceField(ConversionContext ctx, Supplier> typeAndFieldName, Supplier initializer) { + protected void introduceField(ConversionContext ctx, Supplier typeAndFieldName, Supplier initializer) { if (ctx.spyOrMockCall.getParent() instanceof PsiLocalVariable variable) { var field = createField(computeBlocking(() -> variable.getType().getCanonicalText()), getLocalVariableName(variable), initializer, ctx); doIntroduceFieldForLocalVariable(ctx, field, variable); } else { - var typeAndField = typeAndFieldName.get(); - var field = createField(typeAndField.first, typeAndField.second, initializer, ctx); + var typeAndName = typeAndFieldName.get(); + var field = createField(typeAndName.type, typeAndName.name, initializer, ctx); doIntroduceFieldForStandaloneMethodCall(ctx, field); } } @@ -180,9 +173,9 @@ protected void moveOffsetAfter(boolean success) { if (!success) return; Optional.ofNullable(getVariable()) - .filter(renamedVariable -> Arrays.stream(ctx.targetClass.getFields()).noneMatch(f -> renamedVariable.getName().equals(f.getName()))) + .filter(renamedVariable -> none(ctx.targetClass.getFields(), f -> renamedVariable.getName().equals(f.getName()))) .ifPresentOrElse(renamedVariable -> - WriteCommandAction.runWriteCommandAction(ctx.project, () -> { + runWriteCommandAction(ctx.project, () -> { PsiDocumentManager.getInstance(ctx.project).commitAllDocuments(); //set the name of the field to be introduced, so that it is created with the name specified by the user if (renamedVariable.getName() != null) { @@ -197,7 +190,7 @@ protected void moveOffsetAfter(boolean success) { } }.performInplaceRename(); } else { - WriteCommandAction.runWriteCommandAction(ctx.project, () -> { + runWriteCommandAction(ctx.project, () -> { PsiDocumentManager.getInstance(ctx.project).commitAllDocuments(); ctx.targetClass.add(fieldToAdd); ctx.spyOrMockCall.getParent().delete(); //remove local variable declaration @@ -212,11 +205,11 @@ private void doIntroduceFieldForStandaloneMethodCall(ConversionContext ctx, PsiE var manager = PsiDocumentManager.getInstance(ctx.project); ApplicationManager.getApplication().invokeAndWait(manager::commitAllDocuments); var addedField = new Ref(); - WriteCommandAction.runWriteCommandAction(ctx.project, () -> { + runWriteCommandAction(ctx.project, () -> { addedField.set((PsiField) ctx.targetClass.add(field)); manager.commitDocument(ctx.getDocument()); }); - WriteCommandAction.runWriteCommandAction(ctx.project, () -> { + runWriteCommandAction(ctx.project, () -> { //replace standalone spy() call with the name of the new field. ctx.getDocument().replaceString(ctx.spyOrMockCall.getTextOffset(), endOffsetOf(ctx.spyOrMockCall), addedField.get().getName()); manager.commitDocument(ctx.getDocument()); @@ -234,16 +227,17 @@ private void doIntroduceFieldForStandaloneMethodCall(ConversionContext ctx, PsiE *

    * For example: *

    -     * new MockableObject(); -> Pair("MockableObject", "mockableObject")
    -     * new MockableObject<String, Integer>(); -> Pair("MockableObject<java.lang.String, java.lang.Integer>", "mockableObject")
    -     * 
    + * {@code + * new MockableObject(); -> FieldTypeAndName("MockableObject", "mockableObject") + * new MockableObject(); -> FieldTypeAndName("MockableObject", "mockableObject") + * } */ - protected Couple constructFieldTypeAndName(PsiJavaCodeReferenceElement classReference) { + protected FieldTypeAndName constructFieldTypeAndName(PsiJavaCodeReferenceElement classReference) { return computeBlocking(() -> { - String fieldName = classReference.getTypeParameters().length == 0 + String fieldType = classReference.getTypeParameters().length == 0 ? classReference.getReferenceName() : classReference.getReferenceName() + "<" + Arrays.stream(classReference.getTypeParameters()).map(PsiType::getCanonicalText).collect(joining(", ")) + ">"; - return Couple.of(fieldName, toLowerCamel(classReference.getReferenceName())); + return new FieldTypeAndName(fieldType, toLowerCamel(classReference.getReferenceName())); }); } @@ -283,4 +277,7 @@ Document getDocument() { return editor.getDocument(); } } + + protected record FieldTypeAndName(String type, String name) { + } } diff --git a/src/main/java/com/picimako/mockitools/intention/ConvertMockCallToFieldIntention.java b/src/main/java/com/picimako/mockitools/intention/ConvertMockCallToFieldIntention.java index 03891255..8aed5f25 100644 --- a/src/main/java/com/picimako/mockitools/intention/ConvertMockCallToFieldIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/ConvertMockCallToFieldIntention.java @@ -2,9 +2,9 @@ package com.picimako.mockitools.intention; -import static com.google.common.collect.Iterables.getLast; import static com.intellij.openapi.application.ReadAction.computeBlocking; import static com.intellij.openapi.application.ReadAction.runBlocking; +import static com.intellij.util.containers.ContainerUtil.map; import static com.picimako.mockitools.MockableTypesUtil.isMockableTypeInAnyWay; import static com.picimako.mockitools.MockitoMockMatchers.MOCK_WITH_ANSWER; import static com.picimako.mockitools.MockitoMockMatchers.MOCK_WITH_NAME; @@ -16,13 +16,13 @@ import static com.picimako.mockitools.MockitoQualifiedNames.MOCK_MAKER; import static com.picimako.mockitools.MockitoQualifiedNames.NAME; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK; -import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK_SERIALIZABLE_MODE; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCK_SETTINGS; import static com.picimako.mockitools.MockitoQualifiedNames.SERIALIZABLE; import static com.picimako.mockitools.MockitoQualifiedNames.STRICTNESS; import static com.picimako.mockitools.MockitoQualifiedNames.STUB_ONLY; import static com.picimako.mockitools.MockitoQualifiedNames.WITHOUT_ANNOTATIONS; +import static com.picimako.mockitools.MockitoolsPsiUtil.MOCKITO_WITH_SETTINGS; import static com.picimako.mockitools.util.ClassObjectAccessUtil.getOperandType; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromLast; import static com.picimako.mockitools.util.PsiMethodUtil.get2ndArgument; @@ -32,9 +32,7 @@ import static com.picimako.mockitools.util.PsiMethodUtil.hasOneArgument; import static com.picimako.mockitools.util.PsiMethodUtil.hasTwoArguments; import static com.siyeh.ig.callMatcher.CallMatcher.instanceCall; -import static com.siyeh.ig.callMatcher.CallMatcher.staticCall; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; -import static java.util.stream.Collectors.joining; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -52,11 +50,9 @@ import com.intellij.psi.util.PsiTreeUtil; import com.picimako.mockitools.MockitoMockMatchers; import com.picimako.mockitools.MockitoQualifiedNames; -import com.picimako.mockitools.util.PsiClassUtil; import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; import java.util.Set; import java.util.function.Supplier; @@ -94,9 +90,8 @@ * @since 0.4.0 */ final class ConvertMockCallToFieldIntention extends ConvertCallToFieldIntentionBase { - private static final CallMatcher MOCKITO_WITH_SETTINGS = staticCall(ORG_MOCKITO_MOCKITO, "withSettings"); - - private static final CallMatcher MOCK_SETTINGS_SERIALIZABLE_WITH_MODE = instanceCall(ORG_MOCKITO_MOCK_SETTINGS, SERIALIZABLE).parameterTypes(ORG_MOCKITO_MOCK_SERIALIZABLE_MODE); + private static final CallMatcher MOCK_SETTINGS_SERIALIZABLE_WITH_MODE = + instanceCall(ORG_MOCKITO_MOCK_SETTINGS, SERIALIZABLE).parameterTypes(ORG_MOCKITO_MOCK_SERIALIZABLE_MODE); /** * {@code MockSettings.spiedInstance()} could be a special case for converting into a {@code @Spy} field, * but {@code @Mock} doesn't support such configuration, and there is also the actual object instance that is passed in, @@ -105,7 +100,7 @@ final class ConvertMockCallToFieldIntention extends ConvertCallToFieldIntentionB private static final Set SUPPORTED_MOCK_SETTINGS_METHODS = Set.of(DEFAULT_ANSWER, STUB_ONLY, NAME, EXTRA_INTERFACES, LENIENT, STRICTNESS, MOCK_MAKER, WITHOUT_ANNOTATIONS); - public ConvertMockCallToFieldIntention() { + ConvertMockCallToFieldIntention() { super(MockitoQualifiedNames.MOCK, "@Mock"); } @@ -136,7 +131,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file return hasTwoArguments(call) && isSettingsSupportedByMockAnnotation(get2ndArgument(call)); } } - //Generic inferred Mockito.mock() + //Generic-inferred Mockito.mock() else { var parentVariable = PsiTreeUtil.getParentOfType(call, PsiLocalVariable.class); return parentVariable != null @@ -160,14 +155,14 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file private boolean isSettingsSupportedByMockAnnotation(PsiExpression settingsArg) { if (settingsArg instanceof PsiMethodCallExpression) { var settingsCalls = collectCallsInChainFromLast(settingsArg); - return MOCKITO_WITH_SETTINGS.matches(getLast(settingsCalls)) - && settingsCalls.stream() - .limit(settingsCalls.size() - 1L) //Skip analyzing the Mockito.withSettings() call as it has already been checked - .allMatch(call -> { - String methodName = getMethodName(call); - return SUPPORTED_MOCK_SETTINGS_METHODS.contains(methodName) - || (SERIALIZABLE.equals(methodName) && !MOCK_SETTINGS_SERIALIZABLE_WITH_MODE.matches(call)); - }); + return MOCKITO_WITH_SETTINGS.matches(settingsCalls.getLast()) + && settingsCalls.stream() + .limit(settingsCalls.size() - 1L) //Skip analyzing the Mockito.withSettings() call as it has already been checked + .allMatch(call -> { + String methodName = getMethodName(call); + return SUPPORTED_MOCK_SETTINGS_METHODS.contains(methodName) + || (SERIALIZABLE.equals(methodName) && !MOCK_SETTINGS_SERIALIZABLE_WITH_MODE.matches(call)); + }); } return false; } @@ -190,9 +185,10 @@ protected PsiElement createField(String fieldType, String fieldName, Supplier + * {@code * Mockito.mock(Type.class, Answers.RETURNS_DEFAULTS) -> @Mock * Mockito.mock(Type.class, Mockito.withSettings().defaultAnswer(Answers.RETURNS_DEFAULTS)) -> @Mock - * + * } */ private void setMockAnnotationAttributes(PsiField field, ConversionContext ctx) { runBlocking(() -> { @@ -216,16 +212,17 @@ private void configureFromMockSettings(MockSettingsBasedAnnotationConfigurer con for (int i = calls.size() - 2; i >= 0; i--) { var call = calls.get(i); String methodName = getMethodName(call); - if (STUB_ONLY.equals(methodName) - || LENIENT.equals(methodName) - || SERIALIZABLE.equals(methodName) - || WITHOUT_ANNOTATIONS.equals(methodName)) - configurer.configureBooleanAttribute(methodName); - else if (NAME.equals(methodName)) configurer.configureName(call); - else if (DEFAULT_ANSWER.equals(methodName)) configurer.configureAnswerFromCall(call); - else if (EXTRA_INTERFACES.equals(methodName)) configurer.configureExtraInterfaces(call); - else if (STRICTNESS.equals(methodName)) configurer.configureStrictness(call); - else if (MOCK_MAKER.equals(methodName)) configurer.configureMockMaker(call); + switch (methodName) { + case STUB_ONLY, LENIENT, SERIALIZABLE, WITHOUT_ANNOTATIONS -> + configurer.configureBooleanAttribute(methodName); + case NAME -> configurer.configureName(call); + case DEFAULT_ANSWER -> configurer.configureAnswerFromCall(call); + case EXTRA_INTERFACES -> configurer.configureExtraInterfaces(call); + case STRICTNESS -> configurer.configureStrictness(call); + case MOCK_MAKER -> configurer.configureMockMaker(call); + case null, default -> { + } + } } } @@ -238,8 +235,8 @@ public static boolean isDefaultAnswer(PsiExpression answer) { private static boolean isAnswersReturnDefaults(PsiElement element) { return element instanceof PsiEnumConstant constant - && MockitoQualifiedNames.ORG_MOCKITO_ANSWERS.equals(constant.getContainingClass().getQualifiedName()) - && "RETURNS_DEFAULTS".equals(constant.getName()); + && MockitoQualifiedNames.ORG_MOCKITO_ANSWERS.equals(constant.getContainingClass().getQualifiedName()) + && "RETURNS_DEFAULTS".equals(constant.getName()); } /** @@ -274,7 +271,7 @@ private void configureExtraInterfaces(PsiMethodCallExpression call) { if (arguments.length == 1) { setDeclaredAttributeValue(EXTRA_INTERFACES, attributeValue(arguments[0].getText())); } else { - String interfaces = Arrays.stream(arguments).map(arg -> computeBlocking(arg::getText)).collect(joining(",")); + String interfaces = String.join(",", map(arguments, arg -> computeBlocking(arg::getText))); setDeclaredAttributeValue(EXTRA_INTERFACES, attributeValue("{" + interfaces + "}")); } } diff --git a/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallAction.java b/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallAction.java index 12ad2c05..08afda64 100644 --- a/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallAction.java +++ b/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallAction.java @@ -52,7 +52,6 @@ import com.intellij.psi.PsiTypeElement; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiFormatUtilBase; -import com.picimako.mockitools.util.PsiClassUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -85,7 +84,7 @@ final class ConvertMockSpyFieldToCallAction extends AnAction { //Instantiation - public ConvertMockSpyFieldToCallAction(PsiField fieldToConvert, PsiMethod method) { + ConvertMockSpyFieldToCallAction(PsiField fieldToConvert, PsiMethod method) { super( //The element text is always method name and the parameter list, e.g. 'testMethod(String)' computeBlocking(() -> PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, @@ -247,9 +246,9 @@ public static void introduceMockitoMockingCall(PsiField fieldToConvert, PsiMetho } private static Optional valueOf(JvmAnnotationAttribute attribute) { - return computeBlocking(() -> attribute instanceof PsiNameValuePair attributeNameValue && attributeNameValue.getValue() != null - ? Optional.ofNullable(attributeNameValue.getValue()) - : Optional.empty()); + return attribute instanceof PsiNameValuePair attributeNameValue && attributeNameValue.getValue() != null + ? Optional.ofNullable(attributeNameValue.getValue()) + : Optional.empty(); } private static void appendSetting(StringBuilder sb, String methodName, String argument) { @@ -260,13 +259,11 @@ private static void appendSetting(StringBuilder sb, String methodName, String ar * This is to make sure that the mock()/spy() call's argument (when a PsiClassObjectAccessExpression), doesn't include the generic type arguments. */ private static void appendType(PsiField fieldToConvert, StringBuilder mockitoMockingCall) { - runBlocking(() -> { - Optional.ofNullable(fieldToConvert.getTypeElement()) - .map(PsiTypeElement::getInnermostComponentReferenceElement) - .map(PsiQualifiedReference::getReferenceName) - .ifPresentOrElse(mockitoMockingCall::append, () -> mockitoMockingCall.append(fieldToConvert.getType().getCanonicalText())); - mockitoMockingCall.append(".class"); - }); + Optional.ofNullable(fieldToConvert.getTypeElement()) + .map(PsiTypeElement::getInnermostComponentReferenceElement) + .map(PsiQualifiedReference::getReferenceName) + .ifPresentOrElse(mockitoMockingCall::append, () -> mockitoMockingCall.append(fieldToConvert.getType().getCanonicalText())); + mockitoMockingCall.append(".class"); } private static boolean isBlank(PsiAnnotationMemberValue value) { diff --git a/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallIntention.java b/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallIntention.java index 46bfe301..b9fe0b8e 100644 --- a/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/ConvertMockSpyFieldToCallIntention.java @@ -8,7 +8,7 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_SPY; import static com.picimako.mockitools.intention.ConvertMockSpyFieldToCallAction.introduceMockitoMockingCall; import static com.picimako.mockitools.intention.MethodRearranger.reOrder; -import static com.picimako.mockitools.util.ListPopupHelper.showActionsInListPopup; +import static com.picimako.mockitools.intention.ListPopupHelper.showActionsInListPopup; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.util.IntentionFamilyName; diff --git a/src/main/java/com/picimako/mockitools/intention/ConvertSpyCallToFieldIntention.java b/src/main/java/com/picimako/mockitools/intention/ConvertSpyCallToFieldIntention.java index 531a8b35..ce5aab90 100644 --- a/src/main/java/com/picimako/mockitools/intention/ConvertSpyCallToFieldIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/ConvertSpyCallToFieldIntention.java @@ -19,6 +19,7 @@ import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiNewExpression; +import com.intellij.psi.PsiReference; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import com.picimako.mockitools.MockitoQualifiedNames; @@ -72,7 +73,7 @@ */ final class ConvertSpyCallToFieldIntention extends ConvertCallToFieldIntentionBase { - public ConvertSpyCallToFieldIntention() { + ConvertSpyCallToFieldIntention() { super(SPY, "@Spy"); } @@ -141,13 +142,13 @@ private static boolean isDefaultConstructor(@NotNull PsiNewExpression newExpress //new Clazz() + auto-gen Clazz() -> true //new Clazz() + Clazz(String) + Clazz(int) -> false //new Clazz("") + Clazz() + Clazz(int) + Clazz(double) -> false - return Optional.ofNullable(computeBlocking(newExpression::getClassReference)) - .map(ref -> computeBlocking(ref::resolve)) + return computeBlocking(() -> Optional.ofNullable(newExpression.getClassReference()) + .map(PsiReference::resolve) .map(PsiClass.class::cast) .map(PsiClass::getConstructors) .stream() .flatMap(Arrays::stream) - .noneMatch(constr -> !computeBlocking(() -> constr.getParameterList().isEmpty())); + .noneMatch(constr -> !constr.getParameterList().isEmpty())); } /** diff --git a/src/main/java/com/picimako/mockitools/intention/ConvertThrowStubbingArgumentsIntention.java b/src/main/java/com/picimako/mockitools/intention/ConvertThrowStubbingArgumentsIntention.java index fc1a7f4d..9348a8e7 100644 --- a/src/main/java/com/picimako/mockitools/intention/ConvertThrowStubbingArgumentsIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/ConvertThrowStubbingArgumentsIntention.java @@ -22,7 +22,7 @@ import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiNewExpression; import com.intellij.util.IncorrectOperationException; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import com.picimako.mockitools.inspection.consecutive.TypeConversionMethod; import com.picimako.mockitools.inspection.stubbing.ExceptionStubber; import com.picimako.mockitools.resources.MockitoolsBundle; @@ -43,7 +43,7 @@ *
  • BDDMockito.given().willThrow()
  • *
  • Mockito.doThrow().when()
  • *
  • Mockito.do*().doThrow().when()
  • - *
  • BDDMockito.willl*).willThrow().given()
  • + *
  • BDDMockito.will*).willThrow().given()
  • *
  • BDDMockito.willThrow().given()
  • *
* diff --git a/src/main/java/com/picimako/mockitools/util/ListPopupHelper.java b/src/main/java/com/picimako/mockitools/intention/ListPopupHelper.java similarity index 74% rename from src/main/java/com/picimako/mockitools/util/ListPopupHelper.java rename to src/main/java/com/picimako/mockitools/intention/ListPopupHelper.java index 29a0dad1..2a06034d 100644 --- a/src/main/java/com/picimako/mockitools/util/ListPopupHelper.java +++ b/src/main/java/com/picimako/mockitools/intention/ListPopupHelper.java @@ -1,10 +1,11 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools.util; +package com.picimako.mockitools.intention; + +import static com.intellij.openapi.application.ApplicationManager.getApplication; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.project.Project; @@ -32,27 +33,25 @@ public static void selectItemAndRun(String title, List var step = new BaseListPopupStep<>(title, listItems) { @Override public @Nullable PopupStep onChosen(T selectedItem, boolean finalChoice) { - action.consume(selectedItem); - return null; + return doFinalStep(() -> action.consume(selectedItem)); } }; - if (ApplicationManager.getApplication().isUnitTestMode()) { - ApplicationManager.getApplication().invokeAndWait(() -> new ListPopupImpl(project, step) { - @Override - protected ListCellRenderer getListElementRenderer() { - return cellRenderer.get(); - } - }.showInBestPositionFor(editor)); + if (getApplication().isUnitTestMode()) { + getApplication().invokeAndWait(() -> showListPopup(project, step, cellRenderer, editor)); } else { - new ListPopupImpl(project, step) { - @Override - protected ListCellRenderer getListElementRenderer() { - return cellRenderer.get(); - } - }.showInBestPositionFor(editor); + showListPopup(project, step, cellRenderer, editor); } } + private static void showListPopup(Project project, BaseListPopupStep step, Supplier> cellRenderer, Editor editor) { + new ListPopupImpl(project, step) { + @Override + protected ListCellRenderer getListElementRenderer() { + return cellRenderer.get(); + } + }.showInBestPositionFor(editor); + } + /** * Shows a list popup with the provided list of actions. * diff --git a/src/main/java/com/picimako/mockitools/MemberInplaceRenameHelper.java b/src/main/java/com/picimako/mockitools/intention/MemberInplaceRenameHelper.java similarity index 96% rename from src/main/java/com/picimako/mockitools/MemberInplaceRenameHelper.java rename to src/main/java/com/picimako/mockitools/intention/MemberInplaceRenameHelper.java index 079a94a3..cdb34b28 100644 --- a/src/main/java/com/picimako/mockitools/MemberInplaceRenameHelper.java +++ b/src/main/java/com/picimako/mockitools/intention/MemberInplaceRenameHelper.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.intention; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; diff --git a/src/main/java/com/picimako/mockitools/intention/MethodRearranger.java b/src/main/java/com/picimako/mockitools/intention/MethodRearranger.java index a323cc15..67687e0f 100644 --- a/src/main/java/com/picimako/mockitools/intention/MethodRearranger.java +++ b/src/main/java/com/picimako/mockitools/intention/MethodRearranger.java @@ -18,9 +18,9 @@ * @see ConvertMockSpyFieldToCallIntention * @since 0.7.0 */ -public final class MethodRearranger { +final class MethodRearranger { - public static final Set BEFORE_ANNOTATIONS = Set.of( + static final Set BEFORE_ANNOTATIONS = Set.of( //JUnit 4 "org.junit.Before", "org.junit.BeforeClass", //JUnit 5 @@ -30,7 +30,11 @@ public final class MethodRearranger { "org.testng.annotations.BeforeGroups", "org.testng.annotations.BeforeClass", "org.testng.annotations.BeforeMethod" ); - public static final Set TEST_ANNOTATIONS = Set.of("org.junit.Test", "org.junit.jupiter.api.Test", "org.testng.annotations.Test"); + static final Set TEST_ANNOTATIONS = Set.of( + "org.junit.Test", "org.junit.jupiter.api.Test", + "org.junit.jupiter.api.TestFactory", + "org.junit.jupiter.params.ParameterizedTest", + "org.testng.annotations.Test"); /** * Returns a reordered variant of the argument methods array. @@ -42,7 +46,7 @@ public final class MethodRearranger { * * @param methods the methods to reorder */ - public static List reOrder(PsiMethod[] methods) { + static List reOrder(PsiMethod[] methods) { var beforeHooks = new SmartList(); var testMethods = new SmartList(); var restOfMethods = new SmartList(); diff --git a/src/main/java/com/picimako/mockitools/util/PsiClassUtil.java b/src/main/java/com/picimako/mockitools/intention/PsiClassUtil.java similarity index 92% rename from src/main/java/com/picimako/mockitools/util/PsiClassUtil.java rename to src/main/java/com/picimako/mockitools/intention/PsiClassUtil.java index 64d6f29a..39a8165e 100644 --- a/src/main/java/com/picimako/mockitools/util/PsiClassUtil.java +++ b/src/main/java/com/picimako/mockitools/intention/PsiClassUtil.java @@ -1,6 +1,6 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools.util; +package com.picimako.mockitools.intention; import static com.intellij.openapi.application.ReadAction.runBlocking; @@ -30,7 +30,7 @@ public final class PsiClassUtil { *

* Based on {@link com.intellij.refactoring.introduceField.BaseExpressionToFieldHandler}. */ - public static List getParentClasses(@NotNull PsiElement expression) { + static List getParentClasses(@NotNull PsiElement expression) { var parentClasses = new SmartList(); PsiElement parent = Optional.ofNullable(expression.getUserData(ElementToWorkOn.PARENT)).orElseGet(expression::getParent); while (parent != null) { @@ -40,7 +40,7 @@ public static List getParentClasses(@NotNull PsiElement expression) { return parentClasses; } - public static void importClass(String fqn, PsiElement context) { + static void importClass(String fqn, PsiElement context) { runBlocking(() -> { PsiClass collectionClass = JavaPsiFacade.getInstance(context.getProject()).findClass(fqn, ProjectScope.getAllScope(context.getProject())); ImportUtils.addImportIfNeeded(collectionClass, context); diff --git a/src/main/java/com/picimako/mockitools/intention/convert/ConversionIntentionBase.java b/src/main/java/com/picimako/mockitools/intention/convert/ConversionIntentionBase.java index 97b5f140..4e285df0 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/ConversionIntentionBase.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/ConversionIntentionBase.java @@ -13,7 +13,6 @@ import static com.picimako.mockitools.util.PsiMethodUtil.getQualifier; import static com.picimako.mockitools.util.PsiMethodUtil.isIdentifierOfMethodCall; import static com.picimako.mockitools.util.Ranges.charSequenceInRange; -import static com.picimako.mockitools.util.TokenTypes.isTokenType; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.actionSystem.AnAction; @@ -25,15 +24,18 @@ import com.intellij.psi.PsiExpressionStatement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; +import com.intellij.psi.PsiJavaToken; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.tree.IElementType; import com.intellij.util.IncorrectOperationException; +import com.picimako.mockitools.intention.ListPopupHelper; import com.picimako.mockitools.intention.convert.verification.NoActionAvailableAction; -import com.picimako.mockitools.util.ListPopupHelper; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; @@ -47,15 +49,27 @@ @RequiredArgsConstructor(access = AccessLevel.PROTECTED) public abstract class ConversionIntentionBase implements IntentionAction { protected static final List NO_ACTION_AVAILABLE = Collections.singletonList(NoActionAvailableAction.INSTANCE); + /** + * A display name for the stubbing or verification approach. Used in the intention actions' family names. + */ protected final String sourceApproachName; + /** + * Used for bulk conversions to limit the execution of some logic when the selection is shorter than this value + * + * @see isAvailableForBulkConversion + */ private final int minSelectionLength; @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - if (computeBlocking(() -> !editor.getSelectionModel().hasSelection())) { - final var element = file.findElementAt(computeBlocking(() -> editor.getCaretModel().getOffset())); - return isIdentifierOfMethodCall(element) && isAvailableFor(getMethodCallForIdentifier(element)); - } else return isAvailableForBulkConversion(editor, file); + @Nullable final var elementAtCaret = computeBlocking(() -> { + var hasSelection = editor.getSelectionModel().hasSelection(); + return !hasSelection ? file.findElementAt(editor.getCaretModel().getOffset()) : null; + }); + + return elementAtCaret != null + ? isIdentifierOfMethodCall(elementAtCaret) && isAvailableFor(getMethodCallForIdentifier(elementAtCaret)) + : isAvailableForBulkConversion(editor, file); } /** @@ -110,6 +124,13 @@ private boolean isSymbolAfterMethodCallChain(PsiElement element) { || (isTokenType(element, JavaTokenType.SEMICOLON) && getPrevSiblingOfType(element, PsiMethodCallExpression.class) != null); } + /** + * Returns if the element is of the provided type. + */ + private static boolean isTokenType(PsiElement element, IElementType type) { + return element instanceof PsiJavaToken && element.getNode().getElementType() == type; + } + /** * Returns if this intention should be available for the provided method call. */ diff --git a/src/main/java/com/picimako/mockitools/EnforceConventionUtil.java b/src/main/java/com/picimako/mockitools/intention/convert/EnforceConventionUtil.java similarity index 95% rename from src/main/java/com/picimako/mockitools/EnforceConventionUtil.java rename to src/main/java/com/picimako/mockitools/intention/convert/EnforceConventionUtil.java index b4b8c9d5..521968a3 100644 --- a/src/main/java/com/picimako/mockitools/EnforceConventionUtil.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/EnforceConventionUtil.java @@ -1,10 +1,11 @@ //Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.picimako.mockitools; +package com.picimako.mockitools.intention.convert; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.psi.PsiElement; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.inspection.stubbing.EnforceConventionInspection; /** diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntention.java index c89e87b3..c3b15658 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntention.java @@ -3,17 +3,17 @@ package com.picimako.mockitools.intention.convert.stub; import static com.intellij.openapi.application.ReadAction.computeBlocking; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; -import static com.picimako.mockitools.StubbingApproach.BDDMOCKITO_GIVEN; +import static com.picimako.mockitools.dsl.StubbingApproach.BDDMOCKITO_GIVEN; import static com.picimako.mockitools.intention.convert.stub.DoesntContainUnsupportedMethod.DOESNT_CONTAIN_WILL; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import java.util.ArrayList; import java.util.List; @@ -29,7 +29,7 @@ final class ConvertFromBDDMockitoGivenIntention extends ConvertStubbingIntentionBase { public ConvertFromBDDMockitoGivenIntention() { - super("BDDMockito.given()", ORG_MOCKITO_BDDMOCKITO); + super(StubbingApproach.BDDMOCKITO_GIVEN, ORG_MOCKITO_BDDMOCKITO); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntention.java index 2e4aa155..6ee75fd6 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntention.java @@ -3,10 +3,10 @@ package com.picimako.mockitools.intention.convert.stub; import static com.intellij.openapi.application.ReadAction.computeBlocking; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; -import static com.picimako.mockitools.StubbingApproach.BDDMOCKITO_WILL_X; +import static com.picimako.mockitools.dsl.StubbingApproach.BDDMOCKITO_WILL_X; import static com.picimako.mockitools.intention.convert.stub.DoesntContainUnsupportedMethod.DOESNT_CONTAIN_WILL; import static com.picimako.mockitools.intention.convert.stub.DoesntContainUnsupportedMethod.DOESNT_CONTAIN_WILL_DO_NOTHING; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; @@ -15,7 +15,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import java.util.ArrayList; import java.util.List; @@ -31,7 +31,7 @@ final class ConvertFromBDDMockitoWillIntention extends ConvertStubbingIntentionBase { public ConvertFromBDDMockitoWillIntention() { - super("BDDMockito.will*()", ORG_MOCKITO_BDDMOCKITO); + super(StubbingApproach.BDDMOCKITO_WILL_X, ORG_MOCKITO_BDDMOCKITO); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntention.java index 3aea150d..b0aa6e91 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntention.java @@ -3,10 +3,10 @@ package com.picimako.mockitools.intention.convert.stub; import static com.intellij.openapi.application.ReadAction.computeBlocking; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_DO_X; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_DO_X; import static com.picimako.mockitools.intention.convert.stub.DoesntContainUnsupportedMethod.DOESNT_CONTAIN_DO_NOTHING; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; @@ -14,7 +14,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import java.util.ArrayList; import java.util.List; @@ -30,7 +30,7 @@ final class ConvertFromMockitoDoIntention extends ConvertStubbingIntentionBase { public ConvertFromMockitoDoIntention() { - super("Mockito.do*()", ORG_MOCKITO_MOCKITO); + super(StubbingApproach.MOCKITO_DO_X, ORG_MOCKITO_MOCKITO); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntention.java index 9fcf5685..7d0cb1be 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntention.java @@ -3,17 +3,17 @@ package com.picimako.mockitools.intention.convert.stub; import static com.intellij.openapi.application.ReadAction.computeBlocking; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; -import static com.picimako.mockitools.StubbingApproach.MOCKITO_WHEN; +import static com.picimako.mockitools.dsl.StubbingApproach.MOCKITO_WHEN; import static com.picimako.mockitools.intention.convert.stub.DoesntContainUnsupportedMethod.DOESNT_CONTAIN_THEN; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import java.util.ArrayList; import java.util.List; @@ -29,7 +29,7 @@ final class ConvertFromMockitoWhenIntention extends ConvertStubbingIntentionBase { public ConvertFromMockitoWhenIntention() { - super("Mockito.when()", ORG_MOCKITO_MOCKITO); + super(StubbingApproach.MOCKITO_WHEN, ORG_MOCKITO_MOCKITO); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingAction.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingAction.java index 6417d678..6d7e09d5 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingAction.java @@ -18,11 +18,9 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubbingApproach; -import lombok.Getter; +import com.picimako.mockitools.dsl.StubbingApproach; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; import java.util.List; @@ -35,10 +33,8 @@ * @see StubbingConverter * @since 0.6.0 */ -public class ConvertStubbingAction extends AnAction { +class ConvertStubbingAction extends AnAction { private final StubbingApproach from; - @Getter - @TestOnly private final StubbingApproach to; /** * Whether the conversion is caret- (single call chain) or selection-based (one or more call chains). @@ -46,7 +42,7 @@ public class ConvertStubbingAction extends AnAction { private final boolean isBulkMode; private StubbingConverter converter; - public ConvertStubbingAction(StubbingApproach from, StubbingApproach to, boolean isBulkMode) { + ConvertStubbingAction(StubbingApproach from, StubbingApproach to, boolean isBulkMode) { super(to.presentableText); this.from = from; this.to = to; @@ -74,7 +70,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { /** * Performs this action for a single call chain, when the action is invoked under a single caret. */ - private void performAction(PsiMethodCallExpression firstCallInChain) { + private void performAction(@NotNull PsiMethodCallExpression firstCallInChain) { converter.convert(firstCallInChain, from, to); } @@ -93,7 +89,7 @@ private void performActionInBulk(List statementsInSelect } @Nullable - public static PsiMethodCallExpression getFirstCallInChain(PsiExpressionStatement statement) { + static PsiMethodCallExpression getFirstCallInChain(PsiExpressionStatement statement) { return getParentOfType(/*identifier*/ findChildOfType(statement, PsiIdentifier.class), PsiMethodCallExpression.class); } } diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingIntentionBase.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingIntentionBase.java index ebc09faf..2598c60b 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingIntentionBase.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingIntentionBase.java @@ -18,6 +18,7 @@ import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiReferenceExpression; import com.picimako.mockitools.CallChainAnalyzer; +import com.picimako.mockitools.dsl.StubbingApproach; import com.picimako.mockitools.intention.convert.ConversionIntentionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -34,10 +35,14 @@ * @since 0.6.0 */ public abstract class ConvertStubbingIntentionBase extends ConversionIntentionBase { + /** + * The fully qualified name of the Mockito class from which the conversion happens. + * Either {@code org.mockito.Mockito} or {@code org.mockito.BDDMockito}. + */ private final String qualifierType; - protected ConvertStubbingIntentionBase(String sourceApproachName, String qualifierType) { - super(sourceApproachName, 16); + protected ConvertStubbingIntentionBase(StubbingApproach sourceApproachName, @NotNull String qualifierType) { + super(sourceApproachName.presentableText, 16); this.qualifierType = qualifierType; } diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/DoesntContainUnsupportedMethod.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/DoesntContainUnsupportedMethod.java index 6ed43035..d252ed8c 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/DoesntContainUnsupportedMethod.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/DoesntContainUnsupportedMethod.java @@ -15,11 +15,11 @@ *

* It is used to filter out call chains for stubbing approach conversions with no matching alternative in the target approach. */ -public record DoesntContainUnsupportedMethod(String unsupportedMethodName) implements CallChainAnalyzer { - public static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_THEN = new DoesntContainUnsupportedMethod("then"); - public static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_WILL = new DoesntContainUnsupportedMethod("will"); - public static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_DO_NOTHING = new DoesntContainUnsupportedMethod("doNothing"); - public static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_WILL_DO_NOTHING = new DoesntContainUnsupportedMethod("willDoNothing"); +record DoesntContainUnsupportedMethod(String unsupportedMethodName) implements CallChainAnalyzer { + static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_THEN = new DoesntContainUnsupportedMethod("then"); + static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_WILL = new DoesntContainUnsupportedMethod("will"); + static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_DO_NOTHING = new DoesntContainUnsupportedMethod("doNothing"); + static final DoesntContainUnsupportedMethod DOESNT_CONTAIN_WILL_DO_NOTHING = new DoesntContainUnsupportedMethod("willDoNothing"); @Override public boolean analyze(List calls) { diff --git a/src/main/java/com/picimako/mockitools/intention/convert/stub/StubbingConverter.java b/src/main/java/com/picimako/mockitools/intention/convert/stub/StubbingConverter.java index a1e6b8d9..32e7d6a1 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/stub/StubbingConverter.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/stub/StubbingConverter.java @@ -2,7 +2,6 @@ package com.picimako.mockitools.intention.convert.stub; -import static com.google.common.collect.Iterables.getLast; import static com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; @@ -11,17 +10,18 @@ import static com.picimako.mockitools.util.Ranges.endOffsetOf; import static com.siyeh.ig.psiutils.MethodCallUtils.getMethodName; -import java.util.List; - import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.StubType; -import com.picimako.mockitools.StubbingApproach; -import com.picimako.mockitools.util.PsiClassUtil; +import com.picimako.mockitools.dsl.StubType; +import com.picimako.mockitools.dsl.StubbingApproach; +import com.picimako.mockitools.intention.PsiClassUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; /** * Converts stubbing call chains between the different approaches. @@ -37,7 +37,7 @@ public record StubbingConverter(Project project, Document document, PsiFile file * @param from the stubbing approach to convert from * @param to the target stubbing approach to convert to */ - public void convert(PsiMethodCallExpression firstCallInChain, StubbingApproach from, StubbingApproach to) { + public void convert(@NotNull PsiMethodCallExpression firstCallInChain, StubbingApproach from, StubbingApproach to) { //To make sure that converting a chain to itself doesn't happen if (from.hasSameStubTypeAs(to) && from.methodCallStubber.equals(to.methodCallStubber)) return; @@ -59,13 +59,14 @@ public void convert(PsiMethodCallExpression firstCallInChain, StubbingApproach f *

* For example: *

+     * {@code
      * BDDMockito.given(mock.doSomething()).will*();
      * //may become
      * Mockito.when(mock.doSomething()).then*();
-     * 
+ * } */ - private void convertSameType(List calls, StubbingApproach from, StubbingApproach to) { - doBaseConversion(from, to, calls, endOffsetOf(getQualifier(calls.getFirst())), to.getBeginningOfStubbing(from)); + private void convertSameType(@NotNull List calls, StubbingApproach from, StubbingApproach to) { + doBaseConversion(calls, from, to, endOffsetOf(getQualifier(calls.getFirst())), to.getBeginningOfStubbing(from)); } /** @@ -73,23 +74,24 @@ private void convertSameType(List calls, StubbingApproa *

* For example: *

+     * {@code
      * Mockito.when(mock.doSomething()).then*();
      * //may become
      * Mockito.do*().when(mock).doSomething();
-     * 
+ * } */ - private void convertToStubber(List calls, StubbingApproach from, StubbingApproach to) { + private void convertToStubber(@NotNull List calls, StubbingApproach from, StubbingApproach to) { //These have to be saved before the base conversion, so their values are kept properly var stubbedCall = ((PsiMethodCallExpression) getFirstArgument(calls.getFirst())); //mock.doSomething() var stubbedCallQualifier = getQualifier(stubbedCall).getText(); //"mock" var stubbedCallText = stubbedCall.getText(); //"mock.doSomething()" //At this point the example chain becomes 'Mockito.do*();' - doBaseConversion(from, to, calls, endOffsetOf(calls.getFirst()), to.getBeginningOfStubbing(from)); + doBaseConversion(calls, from, to, endOffsetOf(calls.getFirst()), to.getBeginningOfStubbing(from)); //Adds the '.when(mock).doSomething()' part at the end of the call chain, so the example becomes: 'Mockito.do*().when(mock).doSomething();' document.insertString( - endOffsetOf(getLast(calls)), + endOffsetOf(calls.getLast()), "." + to.methodCallStubber + "(" + stubbedCallQualifier + ")" + stubbedCallText.replace(stubbedCallQualifier, "")); } @@ -98,28 +100,29 @@ private void convertToStubber(List calls, StubbingAppro *

* For example: *

+     * {@code
      * Mockito.do*().when(mock).doSomething();
      * //may become
      * BDDMockito.given(mock.doSomething()).will*();
-     * 
+ * } */ - private void convertToStubbing(List calls, StubbingApproach from, StubbingApproach to) { + private void convertToStubbing(@NotNull List calls, StubbingApproach from, StubbingApproach to) { var stubbingMethod = calls.get(calls.size() - 2); //when(mock) int endOffset = endOffsetOf(getQualifier(calls.getFirst())); //end offset of Mockito //BDDMockito.given + ( + mock + .doSomething() + ) - String replacement = to.getBeginningOfStubbing(from) + "(" + getFirstArgument(stubbingMethod).getText() + getLast(calls).getText().replace(stubbingMethod.getText(), "") + ")"; + String replacement = to.getBeginningOfStubbing(from) + "(" + getFirstArgument(stubbingMethod).getText() + calls.getLast().getText().replace(stubbingMethod.getText(), "") + ")"; //At this point the example chain becomes 'BDDMockito.given(mock.doSomething()).will*().when(mock).doSomething();' - doBaseConversion(from, to, calls, endOffset, replacement); + doBaseConversion(calls, from, to, endOffset, replacement); //Removes the '.when(mock).doSomething()' part at the end, and the example becomes: 'BDDMockito.given(mock.doSomething()).will*();' - document.deleteString(endOffsetOf(calls.get(calls.size() - 3)), endOffsetOf(getLast(calls))); + document.deleteString(endOffsetOf(calls.get(calls.size() - 3)), endOffsetOf(calls.getLast())); } //Low-level conversion logic - private void doBaseConversion(StubbingApproach from, StubbingApproach to, List calls, int endOffset, String replacement) { + private void doBaseConversion(@NotNull List calls, StubbingApproach from, StubbingApproach to, int endOffset, String replacement) { replaceBeginningOfChain(calls, endOffset, replacement); PsiClassUtil.importClassAndCommit(to.getStubStarterClassFqn(), project, file, document); convertMethodNames(calls, from, to); @@ -130,11 +133,12 @@ private void doBaseConversion(StubbingApproach from, StubbingApproach to, List

* For example: *

+     * {@code
      * //The following text:
      * Mockito.do*().when(mock).doSomething();
      * //could become:
      * BDDMockito.given(mock.doSomething).do*().when(mock).doSomething();
-     * 
+ * } * * @param calls the call chain * @param endOffset the end offset to replace the text from the start offset of the chain diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationActionBase.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationActionBase.java index 5d0dde8f..e75b8ccf 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationActionBase.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationActionBase.java @@ -5,8 +5,8 @@ import static com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction; import static com.intellij.psi.util.PsiTreeUtil.findChildOfType; import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; +import static com.picimako.mockitools.intention.PsiClassUtil.importClassAndCommit; import static com.picimako.mockitools.intention.convert.FromSelectionDataRetriever.collectStatementsInSelection; -import static com.picimako.mockitools.util.PsiClassUtil.importClassAndCommit; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.getMethodCallAtCaret; import static com.picimako.mockitools.util.PsiMethodUtil.getReferenceNameElement; @@ -15,6 +15,7 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; @@ -23,7 +24,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.MemberInplaceRenameHelper; +import com.picimako.mockitools.intention.MemberInplaceRenameHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,15 +34,16 @@ * Base action class for verification approach conversion. */ public abstract class ConvertVerificationActionBase extends AnAction { - protected Editor editor; - protected PsiDocumentManager documentManager; /** - * Whether the conversion is caret- (single call chain) or selection-based (one or more call chains). + * Whether the conversion is caret- (single call chain - false) or selection-based (one or more call chains - true). */ private final boolean isBulkMode; + protected PsiDocumentManager documentManager; + protected Editor editor; + private Document document; private InOrderVariableCreator inOrderCreator; - protected ConvertVerificationActionBase(String actionText, boolean isBulkMode) { + protected ConvertVerificationActionBase(@NotNull String actionText, boolean isBulkMode) { super(actionText); this.isBulkMode = isBulkMode; } @@ -52,13 +54,15 @@ protected ConvertVerificationActionBase(String actionText, boolean isBulkMode) { public void actionPerformed(@NotNull AnActionEvent e) { editor = e.getData(CommonDataKeys.EDITOR); if (editor == null) return; - PsiFile file = e.getData(CommonDataKeys.PSI_FILE); + document = editor.getDocument(); + + var file = e.getData(CommonDataKeys.PSI_FILE); if (file == null) return; - Project project = e.getProject(); + var project = e.getProject(); runWriteCommandAction(project, () -> { documentManager = PsiDocumentManager.getInstance(project); - inOrderCreator = new InOrderVariableCreator(editor.getDocument()); + inOrderCreator = new InOrderVariableCreator(document); if (isBulkMode) { var statementsInSelection = collectStatementsInSelection(editor, file); var firstVerification = getVerificationCall(statementsInSelection.getFirst()); @@ -93,7 +97,7 @@ protected void performActionInBulk(List statementsInSele protected final void performAndCommitDocument(Runnable runnable) { runnable.run(); - documentManager.commitDocument(editor.getDocument()); + documentManager.commitDocument(document); } //Helpers @@ -103,11 +107,12 @@ protected final void performAndCommitDocument(Runnable runnable) { *

* For example: *

+     * {@code
      * //The following text:
      * Mockito.verify(mock).doSomething();
      * //becomes:
      * BDDMockito.then(mock).doSomething();
-     * 
+ * } * * @param calls the call chain * @param replacement the replacement text @@ -117,9 +122,9 @@ protected void replaceBeginningOfChain(List calls, Stri //end offset of Mockito.verify/BDDMockito.then var verifyOrThen = calls.getFirst(); int endOffset = endOffsetOf(getReferenceNameElement(verifyOrThen)); - performAndCommitDocument(() -> editor.getDocument().replaceString(verifyOrThen.getTextOffset(), endOffset, replacement)); + performAndCommitDocument(() -> document.replaceString(verifyOrThen.getTextOffset(), endOffset, replacement)); if (mockitoClass.length == 1) - importClassAndCommit(mockitoClass[0], verifyOrThen.getProject(), verifyOrThen.getContainingFile(), editor.getDocument()); + importClassAndCommit(mockitoClass[0], verifyOrThen.getProject(), verifyOrThen.getContainingFile(), document); } /** diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationIntentionBase.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationIntentionBase.java index 2abe9a0b..fb142554 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationIntentionBase.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/ConvertVerificationIntentionBase.java @@ -5,7 +5,7 @@ import com.intellij.codeInspection.util.IntentionFamilyName; import com.intellij.codeInspection.util.IntentionName; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.intention.convert.ConversionIntentionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; @@ -20,6 +20,9 @@ */ public abstract class ConvertVerificationIntentionBase extends ConversionIntentionBase { + /** + * The verification approach the conversion is performed from. + */ private final VerificationApproach sourceApproach; protected ConvertVerificationIntentionBase(VerificationApproach sourceApproach, int minSelectionLength) { @@ -29,8 +32,7 @@ protected ConvertVerificationIntentionBase(VerificationApproach sourceApproach, //The shortest option is selecting 'verify(y).z();'. protected ConvertVerificationIntentionBase(VerificationApproach sourceApproach) { - super(sourceApproach.presentableText, 14); - this.sourceApproach = sourceApproach; + this(sourceApproach, 14); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/InOrderVariableCreator.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/InOrderVariableCreator.java index 5ae1fae1..d5079c41 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/InOrderVariableCreator.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/InOrderVariableCreator.java @@ -2,9 +2,8 @@ package com.picimako.mockitools.intention.convert.verification; -import static com.google.common.collect.Iterables.getLast; import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; -import static com.picimako.mockitools.util.PsiClassUtil.importClassAndCommit; +import static com.picimako.mockitools.intention.PsiClassUtil.importClassAndCommit; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; import com.intellij.openapi.editor.Document; @@ -18,12 +17,12 @@ import java.util.List; /** - * Creates {@code InOrder} type local variables in the form of an {@code InOrder inOrder = Mockito.inOrder([mock objects])} call. + * Creates {@code InOrder}-type local variables in the form of an {@code InOrder inOrder = Mockito.inOrder([mock objects])} call. */ public record InOrderVariableCreator(Document document) { /** - * Creates an {@code org.mockito.InOrder} type local variable and inserts it right before the verification call + * Creates an {@code org.mockito.InOrder}-type local variable and inserts it right before the verification call * that is being converted. *

* The arguments of the {@code Mockito.inOrder()} call will be the mock object used in {@code verificationCall}- @@ -37,7 +36,7 @@ public PsiElement createAndAddInOrderVariable(PsiMethodCallExpression verificati } /** - * Creates an {@code org.mockito.InOrder} type local variable and inserts it right before the verification call + * Creates an {@code org.mockito.InOrder}-type local variable and inserts it right before the verification call * that is being converted. * * @param verificationCall the verification call being converted @@ -57,9 +56,9 @@ public PsiElement createAndAddInOrderVariable(PsiMethodCallExpression verificati //Add the new variable to the parent block, before the Mockito.verify()/BDDMockito.then() call chain var parentBlock = getParentOfType(verificationCall, PsiCodeBlock.class); - //'Iterables.getLast(calls)' returns the call expression for the whole call chain. + //'calls.getLast()' returns the call expression for the whole call chain. // The addition has to happen to before the whole call chain, so it has the proper formatting and underlying PSI. - var addedVariable = parentBlock.addBefore(inOrderVariable, getLast(calls).getParent()); + var addedVariable = parentBlock.addBefore(inOrderVariable, calls.getLast().getParent()); PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); return addedVariable; } diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/AddInOrderToBDDMockitoAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/AddInOrderToBDDMockitoAction.java index 308f0224..71c7fe59 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/AddInOrderToBDDMockitoAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/AddInOrderToBDDMockitoAction.java @@ -13,7 +13,7 @@ import com.intellij.psi.PsiExpressionStatement; import com.intellij.psi.PsiLocalVariable; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.MemberInplaceRenameHelper; +import com.picimako.mockitools.intention.MemberInplaceRenameHelper; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationActionBase; import com.picimako.mockitools.resources.MockitoolsBundle; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyAction.java index af615969..24bdd9d5 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyAction.java @@ -3,6 +3,7 @@ package com.picimako.mockitools.intention.convert.verification.bddmockitothen; import static com.intellij.util.containers.ContainerUtil.map; +import static com.picimako.mockitools.dsl.VerificationApproach.INORDER_VERIFY; import static com.picimako.mockitools.intention.convert.FromSelectionDataRetriever.collectMockObjects; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.get2ndArgument; @@ -16,8 +17,8 @@ import com.intellij.psi.PsiExpressionStatement; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.util.TriConsumer; -import com.picimako.mockitools.MemberInplaceRenameHelper; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.intention.MemberInplaceRenameHelper; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationActionBase; import java.util.List; @@ -35,7 +36,7 @@ */ public class ConvertBDDMockitoThenToInOrderVerifyAction extends ConvertVerificationActionBase { public ConvertBDDMockitoThenToInOrderVerifyAction(boolean isBulkMode) { - super("InOrder.verify()", isBulkMode); + super(INORDER_VERIFY.presentableText, isBulkMode); } //Caret based conversion diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToMockitoVerifyAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToMockitoVerifyAction.java index d02245e2..d61771ed 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToMockitoVerifyAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToMockitoVerifyAction.java @@ -5,6 +5,7 @@ import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INORDER; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_VERIFICATION_VERIFICATION_MODE; +import static com.picimako.mockitools.dsl.VerificationApproach.MOCKITO_VERIFY; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.get2ndArgument; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; @@ -27,7 +28,7 @@ */ public class ConvertBDDMockitoThenToMockitoVerifyAction extends ConvertVerificationActionBase { public ConvertBDDMockitoThenToMockitoVerifyAction(boolean isBulkMode) { - super("Mockito.verify()", isBulkMode); + super(MOCKITO_VERIFY.presentableText, isBulkMode); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntention.java index 8812bbf7..ce05f1bd 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntention.java @@ -6,10 +6,10 @@ import static com.intellij.psi.util.PsiTreeUtil.findChildOfType; import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; import static com.intellij.util.containers.ContainerUtil.all; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; -import static com.picimako.mockitools.VerificationApproach.BDDMOCKITO_THEN_SHOULD; +import static com.picimako.mockitools.dsl.VerificationApproach.BDDMOCKITO_THEN_SHOULD; import static com.picimako.mockitools.intention.convert.FromSelectionDataRetriever.collectStatementsInSelection; import static com.picimako.mockitools.util.MockitoolsContainerUtil.none; import static com.picimako.mockitools.util.PsiMethodUtil.getMethodCallAtCaret; diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntention.java index 2e246c7d..124fdc22 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntention.java @@ -5,10 +5,10 @@ import static com.intellij.openapi.application.ReadAction.computeBlocking; import static com.intellij.psi.util.PsiTreeUtil.findChildOfType; import static com.intellij.psi.util.PsiTreeUtil.getParentOfType; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_INORDER; -import static com.picimako.mockitools.VerificationApproach.INORDER_VERIFY; +import static com.picimako.mockitools.dsl.VerificationApproach.INORDER_VERIFY; import static com.picimako.mockitools.intention.convert.FromSelectionDataRetriever.collectStatementsInSelection; import static com.picimako.mockitools.util.PsiMethodUtil.getMethodCallForIdentifier; import static com.siyeh.ig.psiutils.TypeUtils.typeEquals; @@ -19,7 +19,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationIntentionBase; import java.util.ArrayList; diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToBDDMockitoThenWithInOrderAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToBDDMockitoThenWithInOrderAction.java index c2b2b279..b64d422f 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToBDDMockitoThenWithInOrderAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToBDDMockitoThenWithInOrderAction.java @@ -40,13 +40,14 @@ protected void perform(PsiMethodCallExpression inOrderVerify, Project project) { //E.g. 'inOrder.verify(mock)' becomes 'BDDMockito.then(mock).should()' //E.g. 'inOrder.verify(mock, times(2))' becomes 'BDDMockito.then(mock).should(, times(2))' int endOffsetOfMockArgument = endOffsetOf(getFirstArgument(inOrderVerify)); - if (hasTwoArguments(inOrderVerify)) { - editor.getDocument().replaceString( - endOffsetOfMockArgument, - get2ndArgument(inOrderVerify).getTextOffset(), - ").should(" + inOrderVariableName + ", "); - } else { - editor.getDocument().replaceString(endOffsetOfMockArgument, endOffsetOfMockArgument, ").should(" + inOrderVariableName); - } + + var hasTwoArguments = hasTwoArguments(inOrderVerify); + var endOffset = hasTwoArguments + ? get2ndArgument(inOrderVerify).getTextOffset() + : endOffsetOfMockArgument; + var replacement = hasTwoArguments + ? ").should(" + inOrderVariableName + ", " + : ").should(" + inOrderVariableName; + editor.getDocument().replaceString(endOffsetOfMockArgument, endOffset, replacement); } } diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockedStaticVerifyAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockedStaticVerifyAction.java index 910e3101..078ca897 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockedStaticVerifyAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockedStaticVerifyAction.java @@ -2,6 +2,7 @@ package com.picimako.mockitools.intention.convert.verification.inorderverify; +import static com.picimako.mockitools.dsl.VerificationApproach.MOCKED_STATIC_VERIFY; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; @@ -20,7 +21,7 @@ public class ConvertInOrderVerifyToMockedStaticVerifyAction extends ConvertVerificationActionBase { protected ConvertInOrderVerifyToMockedStaticVerifyAction(boolean isBulkMode) { - super("MockedStatic.verify()", isBulkMode); + super(MOCKED_STATIC_VERIFY.presentableText, isBulkMode); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockitoVerifyAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockitoVerifyAction.java index 795e800d..6fdc0ae5 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockitoVerifyAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertInOrderVerifyToMockitoVerifyAction.java @@ -3,11 +3,12 @@ package com.picimako.mockitools.intention.convert.verification.inorderverify; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; +import static com.picimako.mockitools.dsl.VerificationApproach.MOCKITO_VERIFY; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.util.PsiClassUtil; +import com.picimako.mockitools.intention.PsiClassUtil; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationActionBase; /** @@ -21,7 +22,7 @@ public class ConvertInOrderVerifyToMockitoVerifyAction extends ConvertVerificationActionBase { public ConvertInOrderVerifyToMockitoVerifyAction(boolean isBulkMode) { - super("Mockito.verify()", isBulkMode); + super(MOCKITO_VERIFY.presentableText, isBulkMode); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntention.java index 9b06034c..37e40043 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntention.java @@ -9,7 +9,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiFile; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationIntentionBase; import com.siyeh.ig.psiutils.TypeUtils; diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertMockedStaticVerifyToInOrderVerifyAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertMockedStaticVerifyToInOrderVerifyAction.java index b55cc0e4..e0eba256 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertMockedStaticVerifyToInOrderVerifyAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertMockedStaticVerifyToInOrderVerifyAction.java @@ -3,6 +3,7 @@ package com.picimako.mockitools.intention.convert.verification.mockedstaticverify; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; +import static com.picimako.mockitools.dsl.VerificationApproach.INORDER_VERIFY; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import static com.picimako.mockitools.util.PsiMethodUtil.getFirstArgument; import static com.picimako.mockitools.util.PsiMethodUtil.getQualifier; @@ -37,13 +38,15 @@ */ public class ConvertMockedStaticVerifyToInOrderVerifyAction extends ConvertVerificationActionBase { + private static final CallMatcher.Simple ORG_MOCKITO_MOCKITO_MOCKSTATIC = + staticCall(ORG_MOCKITO_MOCKITO, "mockStatic"); private static final CallMatcher MOCK_STATIC = CallMatcher.anyOf( - staticCall(ORG_MOCKITO_MOCKITO, "mockStatic").parameterCount(1), - staticCall(ORG_MOCKITO_MOCKITO, "mockStatic").parameterCount(2) + ORG_MOCKITO_MOCKITO_MOCKSTATIC.parameterCount(1), + ORG_MOCKITO_MOCKITO_MOCKSTATIC.parameterCount(2) ); public ConvertMockedStaticVerifyToInOrderVerifyAction(boolean isBulkMode) { - super("InOrder.verify()", isBulkMode); + super(INORDER_VERIFY.presentableText, isBulkMode); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntention.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntention.java index ef162598..7d9c585c 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntention.java @@ -3,8 +3,8 @@ package com.picimako.mockitools.intention.convert.verification.mockitoverify; import static com.intellij.openapi.application.ReadAction.computeBlocking; -import static com.picimako.mockitools.EnforceConventionUtil.isBDDMockitoEnforced; -import static com.picimako.mockitools.EnforceConventionUtil.isMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isBDDMockitoEnforced; +import static com.picimako.mockitools.intention.convert.EnforceConventionUtil.isMockitoEnforced; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_MOCKITO; import com.intellij.openapi.actionSystem.AnAction; @@ -13,7 +13,7 @@ import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReferenceExpression; -import com.picimako.mockitools.VerificationApproach; +import com.picimako.mockitools.dsl.VerificationApproach; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationIntentionBase; import java.util.ArrayList; diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithInOrderAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithInOrderAction.java index 1fe0b46d..5b4c5b7f 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithInOrderAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithInOrderAction.java @@ -16,7 +16,7 @@ import com.intellij.psi.PsiExpressionStatement; import com.intellij.psi.PsiMethodCallExpression; import com.intellij.psi.PsiNamedElement; -import com.picimako.mockitools.MemberInplaceRenameHelper; +import com.picimako.mockitools.intention.MemberInplaceRenameHelper; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationActionBase; import com.picimako.mockitools.resources.MockitoolsBundle; @@ -62,13 +62,14 @@ protected void performActionInBulk(List statementsInSele private void performConversion(PsiElement inOrderVariable, PsiMethodCallExpression verificationCall, List calls) { replaceBeginningOfChain(calls, "BDDMockito.then", ORG_MOCKITO_BDDMOCKITO); int endOffsetOfMockArgument = endOffsetOf(getFirstArgument(verificationCall)); - if (hasTwoArguments(verificationCall)) { - editor.getDocument().replaceString( - endOffsetOfMockArgument, - get2ndArgument(verificationCall).getTextOffset(), - ").should(" + ((PsiNamedElement) inOrderVariable.getFirstChild()).getName() + ", "); - } else { - editor.getDocument().replaceString(endOffsetOfMockArgument, endOffsetOfMockArgument, ").should(" + ((PsiNamedElement) inOrderVariable.getFirstChild()).getName()); - } + + var hasTwoArguments = hasTwoArguments(verificationCall); + var endOffset = hasTwoArguments + ? get2ndArgument(verificationCall).getTextOffset() + : endOffsetOfMockArgument; + var replacement = hasTwoArguments + ? ").should(" + ((PsiNamedElement) inOrderVariable.getFirstChild()).getName() + ", " + : ").should(" + ((PsiNamedElement) inOrderVariable.getFirstChild()).getName(); + editor.getDocument().replaceString(endOffsetOfMockArgument, endOffset, replacement); } } diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithoutInOrderAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithoutInOrderAction.java index 8f008237..f3862177 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithoutInOrderAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToBDDMockitoThenWithoutInOrderAction.java @@ -2,7 +2,6 @@ package com.picimako.mockitools.intention.convert.verification.mockitoverify; -import static com.google.common.collect.Iterables.getLast; import static com.picimako.mockitools.MockitoQualifiedNames.ORG_MOCKITO_BDDMOCKITO; import static com.picimako.mockitools.MockitoolsPsiUtil.MOCKITO_VERIFY; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; @@ -41,7 +40,7 @@ protected void perform(PsiMethodCallExpression mockitoVerify, Project project) { //Replace Mockito.verify with BDDMockito.then replaceBeginningOfChain(calls, "BDDMockito.then", ORG_MOCKITO_BDDMOCKITO); //Insert should() after BDDMockito.then - performAndCommitDocument(() -> editor.getDocument().insertString(getReferenceNameElement(getLast(calls)).getTextOffset() - 1, ".should()")); + performAndCommitDocument(() -> editor.getDocument().insertString(getReferenceNameElement(calls.getLast()).getTextOffset() - 1, ".should()")); } else if (MOCKITO_VERIFY_MOCK_MODE.matches(mockitoVerify)) { //It replaces the text between the two arguments of Mockito.verify() with the text ").should(" //E.g. 'Mockito.verify(mock, times(2))' becomes 'Mockito.verify(mock).should(times(2))' diff --git a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToInOrderVerifyAction.java b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToInOrderVerifyAction.java index cdccf708..4b66ab87 100644 --- a/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToInOrderVerifyAction.java +++ b/src/main/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertMockitoVerifyToInOrderVerifyAction.java @@ -2,13 +2,14 @@ package com.picimako.mockitools.intention.convert.verification.mockitoverify; +import static com.picimako.mockitools.dsl.VerificationApproach.INORDER_VERIFY; import static com.picimako.mockitools.intention.convert.FromSelectionDataRetriever.collectMockObjects; import static com.picimako.mockitools.util.PsiMethodUtil.collectCallsInChainFromFirst; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiExpressionStatement; import com.intellij.psi.PsiMethodCallExpression; -import com.picimako.mockitools.MemberInplaceRenameHelper; +import com.picimako.mockitools.intention.MemberInplaceRenameHelper; import com.picimako.mockitools.intention.convert.verification.ConvertVerificationActionBase; import java.util.List; @@ -25,7 +26,7 @@ */ public class ConvertMockitoVerifyToInOrderVerifyAction extends ConvertVerificationActionBase { public ConvertMockitoVerifyToInOrderVerifyAction(boolean isBulkMode) { - super("InOrder.verify()", isBulkMode); + super(INORDER_VERIFY.presentableText, isBulkMode); } @Override diff --git a/src/main/java/com/picimako/mockitools/intention/mocking/ExpandMockCreationIntention.java b/src/main/java/com/picimako/mockitools/intention/mocking/ExpandMockCreationIntention.java index 263547c5..a30a454b 100644 --- a/src/main/java/com/picimako/mockitools/intention/mocking/ExpandMockCreationIntention.java +++ b/src/main/java/com/picimako/mockitools/intention/mocking/ExpandMockCreationIntention.java @@ -75,22 +75,21 @@ public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws if (computeBlocking(() -> MOCKITO_SPY_T.matches(call))) { expand(call, project, () -> { var spiedInstance = getFirstArgument(call); - return String.format( - "org.mockito.Mockito.mock(%s.class, org.mockito.Mockito.withSettings().spiedInstance(%s))", - getRawType(spiedInstance.getType()).getCanonicalText(), spiedInstance.getText()); + return "org.mockito.Mockito.mock(%s.class, org.mockito.Mockito.withSettings().spiedInstance(%s))" + .formatted(getRawType(spiedInstance.getType()).getCanonicalText(), spiedInstance.getText()); }); } //Mockito.mock(.class, ) -> Mockito.mock(.class, withSettings().name()) else if (computeBlocking(() -> MOCK_WITH_NAME.matches(call))) { - expand(call, project, () -> String.format( - "org.mockito.Mockito.mock(%s, org.mockito.Mockito.withSettings().name(%s))", - getFirstArgument(call).getText(), get2ndArgument(call).getText())); + expand(call, project, () -> + "org.mockito.Mockito.mock(%s, org.mockito.Mockito.withSettings().name(%s))" + .formatted(getFirstArgument(call).getText(), get2ndArgument(call).getText())); } //Mockito.mock(.class, ) -> Mockito.mock(.class, withSettings().defaultAnswer()) else if (computeBlocking(() -> MOCK_WITH_ANSWER.matches(call))) { - expand(call, project, () -> String.format( - "org.mockito.Mockito.mock(%s, org.mockito.Mockito.withSettings().defaultAnswer(%s))", - getFirstArgument(call).getText(), get2ndArgument(call).getText())); + expand(call, project, () -> + "org.mockito.Mockito.mock(%s, org.mockito.Mockito.withSettings().defaultAnswer(%s))" + .formatted(getFirstArgument(call).getText(), get2ndArgument(call).getText())); } }); } diff --git a/src/main/java/com/picimako/mockitools/util/ExceptionUtil.java b/src/main/java/com/picimako/mockitools/util/ExceptionUtil.java deleted file mode 100644 index c54c6f30..00000000 --- a/src/main/java/com/picimako/mockitools/util/ExceptionUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -//Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - -package com.picimako.mockitools.util; - -import com.intellij.psi.CommonClassNames; -import com.intellij.psi.PsiClassObjectAccessExpression; -import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiType; -import com.intellij.psi.util.InheritanceUtil; -import org.jetbrains.annotations.Nullable; - -/** - * Utility for exceptions. - */ -public final class ExceptionUtil { - - /** - * Returns whether the exception class referenced by the argument expression is a checked exception. - */ - public static boolean isCheckedException(PsiExpression expression) { - return expression instanceof PsiClassObjectAccessExpression - ? isCheckedException(ClassObjectAccessUtil.getOperandType(expression)) - : isCheckedException(expression.getType()); - } - - /** - * Returns whether the argument type is a checked exception. - */ - private static boolean isCheckedException(@Nullable PsiType type) { - return !InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION) - && !InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ERROR); - } - - private ExceptionUtil() { - //Utility class - } -} diff --git a/src/main/java/com/picimako/mockitools/util/MockitoolsContainerUtil.java b/src/main/java/com/picimako/mockitools/util/MockitoolsContainerUtil.java index 83d5961c..f2828f20 100644 --- a/src/main/java/com/picimako/mockitools/util/MockitoolsContainerUtil.java +++ b/src/main/java/com/picimako/mockitools/util/MockitoolsContainerUtil.java @@ -11,6 +11,9 @@ */ public final class MockitoolsContainerUtil { + /** + * Returns if the {@code iterable} doesn't contain any item matching the {@code predicate}. + */ public static boolean none(@NotNull Iterable iterable, @NotNull Predicate predicate) { for (T t : iterable) { if (predicate.test(t)) return false; @@ -19,6 +22,17 @@ public static boolean none(@NotNull Iterable iterable, @NotNull return true; } + /** + * Returns if the {@code array} doesn't contain any item matching the {@code predicate}. + */ + public static boolean none(T @NotNull [] array, @NotNull Predicate predicate) { + for (T t : array) { + if (predicate.test(t)) return false; + } + + return true; + } + private MockitoolsContainerUtil() { //Utility class } diff --git a/src/main/java/com/picimako/mockitools/util/PsiMethodUtil.java b/src/main/java/com/picimako/mockitools/util/PsiMethodUtil.java index b583ea69..413e9eb1 100644 --- a/src/main/java/com/picimako/mockitools/util/PsiMethodUtil.java +++ b/src/main/java/com/picimako/mockitools/util/PsiMethodUtil.java @@ -219,6 +219,7 @@ public static Optional findCallDownwardsInChain(@NotNul *

* Thus, the calls will be in a reverse order compared to the order they are actually called. */ + @NotNull public static List collectCallsInChainFromLast(@NotNull PsiExpression lastCallInChain) { var calls = new SmartList(lastCallInChain); PsiElement current = lastCallInChain; @@ -231,15 +232,18 @@ public static List collectCallsInChainFromLast(@NotNull return map(calls, PsiMethodCallExpression.class::cast); } - public static List collectCallsInChainFromFirst(PsiExpression expression, boolean includeMySelf) { + @NotNull + public static List collectCallsInChainFromFirst(@NotNull PsiExpression expression, boolean includeMySelf) { return PsiTreeUtil.collectParents(expression, PsiMethodCallExpression.class, includeMySelf, e -> e instanceof PsiExpressionList || e instanceof PsiStatement); } + @NotNull public static List collectCallsInChainFromFirst(PsiMethodCallExpression expression) { return collectCallsInChainFromFirst(expression, false); } + @NotNull public static List collectCallsInChainFromFirst(PsiExpression expression) { return collectCallsInChainFromFirst(expression, false); } diff --git a/src/main/java/com/picimako/mockitools/util/TokenTypes.java b/src/main/java/com/picimako/mockitools/util/TokenTypes.java deleted file mode 100644 index 2c09323c..00000000 --- a/src/main/java/com/picimako/mockitools/util/TokenTypes.java +++ /dev/null @@ -1,24 +0,0 @@ -//Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - -package com.picimako.mockitools.util; - -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiJavaToken; -import com.intellij.psi.tree.IElementType; - -/** - * Utility for token types. - */ -public final class TokenTypes { - - /** - * Returns if the element is of the provided type. - */ - public static boolean isTokenType(PsiElement element, IElementType type) { - return element instanceof PsiJavaToken && element.getNode().getElementType() == type; - } - - private TokenTypes() { - //Utility class - } -} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 03279c16..615cfa77 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -13,169 +13,169 @@ + id="mockitools.inconsistentArgumentMatcherUsage" + displayName="Inconsistent argument matcher usage" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.verificationModeValuesBetweenLimits" + displayName="Verification mode values are out of bounds" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.captorFieldInitialization" + displayName="@Captor field is explicitly initialized" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.captorFieldOfTypeArgumentCaptor" + displayName="@Captor field is not of the type ArgumentCaptor" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.timesVerificationMode" + displayName="Mockito.times() argument validations" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.extraInterfaces" + displayName="@Mock#extraInterfaces and MockSettings.extraInterfaces() validations" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> - - - - - + + + + + + id="mockitools.mockType" + displayName="Mocking/spying non-mockable types" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.callOnMockitoReset" + displayName="Call on Mockito.reset()" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.noMethodCallArgument" + displayName="No Mockito method call argument specified" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.methodSignatureBasedThrowsExceptionStubbing" + displayName="Checked exception stubbing based on method throws clause" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.simplifyConsecutiveReturnStubbingCalls" + displayName="Consecutive *Return() stubbing calls can be simplified" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.simplifyConsecutiveThrowStubbingCalls" + displayName="Consecutive *Throw() stubbing calls can be simplified" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.cannotStubMethod" + displayName="Stubbing of private/native/equals()/hashCode methods" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.enforce.convention" + displayName="Enforce conventions" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="false" level="ERROR"/> + id="mockitools.single.inorder.reference" + displayName="InOrder variable is used only once" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.unused.inorder.mock" + displayName="Unused or unconfigured mock objects in InOrder verifications" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="WARNING"/> + id="mockitools.stubbing.method.return.type.mismatch" + displayName="Stubbing call's and stubbed method's return type don't match" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.cannot.verify.to.string" + displayName="'toString()' call in verification" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.stub.only.mock.in.verification" + displayName="Stub-only mock used in verification" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.spy.on.mock" + displayName="Spying on mock objects" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.arg.in.generic.inferred.mock.spy.creation" + displayName="Argument in generic inferred mock()/spy() creation" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.arg.in.generic.inferred.captor.creation" + displayName="Argument in generic inferred ArgumentCaptor creation" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.mock.spied.instance.type.mismatch" + displayName="Mocked type and spied instance type mismatch" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="ERROR"/> + id="mockitools.simplify.mock.creation" + displayName="Simplify mock creation" + language="JAVA" groupPath="Java" groupName="Mockito" enabledByDefault="true" level="INFO"/> + id="mockitools.aid.v4.useArgumentMatchers" + displayName="Argument matchers are used via Matchers" + language="JAVA" groupPath="Java,Mockito" groupName="Migration aids (v4)" enabledByDefault="false" level="WARNING"/> + id="mockitools.aid.v4.useAny" + displayName="Using deprecated anyX() matcher" + language="JAVA" groupPath="Java,Mockito" groupName="Migration aids (v4)" enabledByDefault="false" level="WARNING"/> + id="mockitools.aid.v4.use.non.param.null" + displayName="Using parameterized null matcher" + language="JAVA" groupPath="Java,Mockito" groupName="Migration aids (v4)" enabledByDefault="false" level="WARNING"/> + id="mockitools.aid.v4.mocked.static.verify" + displayName="Using deprecated Mockito and MockedStatic.verify" + language="JAVA" groupPath="Java,Mockito" groupName="Migration aids (v4)" enabledByDefault="false" level="WARNING"/> + id="mockitools.aid.v4.plugin.classes" + displayName="Using deprecated plugins classes" + language="JAVA" groupPath="Java,Mockito" groupName="Migration aids (v4)" enabledByDefault="false" level="WARNING"/> + id="mockitools.aid.v4.junit.runners" + displayName="Using deprecated JUnit runner" + language="JAVA" groupPath="Java,Mockito" groupName="Migration aids (v4)" enabledByDefault="false" level="WARNING"/> + id="mockitools.framework.notExtensibleClass" + displayName="Class extends @NotExtensible class" + language="JAVA" groupPath="Java,Mockito" groupName="Framework" enabledByDefault="false" level="WARNING"/> @@ -240,19 +240,22 @@ JAVA mockito.intention.category - com.picimako.mockitools.intention.convert.verification.mockitoverify.ConvertFromMockitoVerifyIntention + + com.picimako.mockitools.intention.convert.verification.mockitoverify.ConvertFromMockitoVerifyIntention JAVA mockito.intention.category - com.picimako.mockitools.intention.convert.verification.bddmockitothen.ConvertFromBDDMockitoThenIntention + + com.picimako.mockitools.intention.convert.verification.bddmockitothen.ConvertFromBDDMockitoThenIntention JAVA mockito.intention.category - com.picimako.mockitools.intention.convert.verification.inorderverify.ConvertFromInOrderVerifyIntention + + com.picimako.mockitools.intention.convert.verification.inorderverify.ConvertFromInOrderVerifyIntention @@ -263,6 +266,7 @@ - + diff --git a/src/main/resources/inspectionDescriptions/ArgumentMatchersCalledViaMatchers.html b/src/main/resources/inspectionDescriptions/ArgumentMatchersCalledViaMatchers.html index 633ac58b..a02d5a80 100644 --- a/src/main/resources/inspectionDescriptions/ArgumentMatchersCalledViaMatchers.html +++ b/src/main/resources/inspectionDescriptions/ArgumentMatchersCalledViaMatchers.html @@ -4,9 +4,6 @@ quick fix to replace the call to be via org.mockito.ArgumentMatchers.

Examples:

-

- The inspection is applied only when the Mockito version included on the module classpath of the currently - inspected file is 2.x or 3.x.

 
 //from:
diff --git a/src/main/resources/inspectionDescriptions/CallOnMockitoReset.html b/src/main/resources/inspectionDescriptions/CallOnMockitoReset.html
index 10682501..119aa538 100644
--- a/src/main/resources/inspectionDescriptions/CallOnMockitoReset.html
+++ b/src/main/resources/inspectionDescriptions/CallOnMockitoReset.html
@@ -6,7 +6,7 @@
 doesn't take into account those cases.
 

Based on Mockito's documentation on resetting mocks + href="https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#resetting_mocks">resetting mocks

"Smart Mockito users hardly use this feature because they know it could be a sign of poor tests. Normally, you don't need to reset your mocks, just create new mocks for each test method."
diff --git a/src/main/resources/inspectionDescriptions/InconsistentArgumentMatcherUsage.html b/src/main/resources/inspectionDescriptions/InconsistentArgumentMatcherUsage.html
index 23fac483..df3e6da2 100644
--- a/src/main/resources/inspectionDescriptions/InconsistentArgumentMatcherUsage.html
+++ b/src/main/resources/inspectionDescriptions/InconsistentArgumentMatcherUsage.html
@@ -24,9 +24,9 @@
 
 

You can find more information in the official documents: - Argument - matchers documentation, - ArgumentMatchers - javadoc + + Argument matchers documentation, + + ArgumentMatchers Javadoc diff --git a/src/main/resources/inspectionDescriptions/MockType.html b/src/main/resources/inspectionDescriptions/MockType.html index 7b10fdc4..bcf26e3a 100644 --- a/src/main/resources/inspectionDescriptions/MockType.html +++ b/src/main/resources/inspectionDescriptions/MockType.html @@ -5,9 +5,9 @@ Mockito.mock() and Mockito.spy() calls.

Based on the logic in Mockito's - InlineDelegateByteBuddyMockMaker#isTypeMockable(Class) + InlineDelegateByteBuddyMockMaker#isTypeMockable(Class) method and - InlineBytecodeGenerator#EXCLUDES + InlineBytecodeGenerator#EXCLUDES field, when attempting to create mocks for primitives, primitive wrapper types, String and Class, Mockito throws an exception that mock creation cannot happen.

diff --git a/src/main/resources/inspectionDescriptions/NotExtensibleClass.html b/src/main/resources/inspectionDescriptions/NotExtensibleClass.html index 44aa9424..8ffc4a39 100644 --- a/src/main/resources/inspectionDescriptions/NotExtensibleClass.html +++ b/src/main/resources/inspectionDescriptions/NotExtensibleClass.html @@ -3,7 +3,7 @@ Reports interfaces and named classes that are inherited from types annotated as @NotExtensible.

According to the javadoc of the @NotExtensible + href="https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/NotExtensible.html">@NotExtensible annotation, it

Indicates to the user that she should not provide custom implementations of given type.
diff --git a/src/main/resources/inspectionDescriptions/ParameterizedIsNotNullMatcher.html b/src/main/resources/inspectionDescriptions/ParameterizedIsNotNullMatcher.html index 258c5e21..165c2a5e 100644 --- a/src/main/resources/inspectionDescriptions/ParameterizedIsNotNullMatcher.html +++ b/src/main/resources/inspectionDescriptions/ParameterizedIsNotNullMatcher.html @@ -5,9 +5,6 @@

Accordingly, it also provides a quick fix to replace them with their non-parameterized variants. -

- The inspection is applied only when the Mockito version included on the module classpath of the currently - inspected file is 2.x or 3.x.

Examples:

diff --git a/src/main/resources/inspectionDescriptions/SpyOnMock.html b/src/main/resources/inspectionDescriptions/SpyOnMock.html
index c6672e0e..604f7ab3 100644
--- a/src/main/resources/inspectionDescriptions/SpyOnMock.html
+++ b/src/main/resources/inspectionDescriptions/SpyOnMock.html
@@ -13,10 +13,5 @@
 Mockito.spy(<object annotated with @Mock>)
 
 
-

- The corresponding feature was introduced in Mockito 5.4.0, but this inspection does not do a library version check, - and validates test code regardless of the Mockito version. -

- NOTE: variables with Mockito.mock() initializers are not recognized as mocks yet. \ No newline at end of file diff --git a/src/main/resources/inspectionDescriptions/StubbingAndMethodReturnTypeMismatch.html b/src/main/resources/inspectionDescriptions/StubbingAndMethodReturnTypeMismatch.html index 5ff711fc..a5decf9d 100644 --- a/src/main/resources/inspectionDescriptions/StubbingAndMethodReturnTypeMismatch.html +++ b/src/main/resources/inspectionDescriptions/StubbingAndMethodReturnTypeMismatch.html @@ -4,14 +4,14 @@ Reports doNothing() and willDoNothing() calls when the stubbed method's return type is void.

Based on Mockito's corresponding - error handling, Only void methods can doNothing(). + href="https://github.com/mockito/mockito/blob/main/mockito-core/src/main/java/org/mockito/internal/exceptions/Reporter.java#L626"> + corresponding error handling, Only void methods can doNothing().

Also reports *Return() calls when the stubbed method's return type is void.

This is based on Mockito's corresponding - error handling, + href="https://github.com/mockito/mockito/blob/main/mockito-core/src/main/java/org/mockito/internal/exceptions/Reporter.java#L601"> + corresponding error handling,

It highlights every instance of doNothing(), willDoNothing() and *Return() calls in the affected call chains. diff --git a/src/main/resources/inspectionDescriptions/TimesVerificationMode.html b/src/main/resources/inspectionDescriptions/TimesVerificationMode.html index f761fb34..930f523c 100644 --- a/src/main/resources/inspectionDescriptions/TimesVerificationMode.html +++ b/src/main/resources/inspectionDescriptions/TimesVerificationMode.html @@ -5,7 +5,7 @@

  • times(0) calls can be replaced with Mockito.never() for better readability,
  • times(1) calls can be removed since 1 is the default value when using times() verification. See Mockito + href="https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#exact_verification">Mockito exact verification documentation.
  • diff --git a/src/main/resources/inspectionDescriptions/UsageOfAnyMatchers.html b/src/main/resources/inspectionDescriptions/UsageOfAnyMatchers.html index 76135e46..03a66062 100644 --- a/src/main/resources/inspectionDescriptions/UsageOfAnyMatchers.html +++ b/src/main/resources/inspectionDescriptions/UsageOfAnyMatchers.html @@ -11,9 +11,6 @@ and anySetOf() with their anyX() variants. -

    - The inspection is applied only when the Mockito version included on the module classpath of the currently - inspected file is 2.x or 3.x.

    anyObject/anyVararg -> any

    diff --git a/src/main/resources/inspectionDescriptions/UsageOfDeprecatedPluginClasses.html b/src/main/resources/inspectionDescriptions/UsageOfDeprecatedPluginClasses.html
    index 4d2a8fce..84de532f 100644
    --- a/src/main/resources/inspectionDescriptions/UsageOfDeprecatedPluginClasses.html
    +++ b/src/main/resources/inspectionDescriptions/UsageOfDeprecatedPluginClasses.html
    @@ -5,9 +5,6 @@
     
     

    Quick fixes can be applied to replace these references to the ones recommended to use. -

    - The inspection is applied only when the Mockito version included on the module classpath of the currently - inspected file is 2.x or 3.x.

    AnnotationEngine

    diff --git a/src/main/resources/inspectionDescriptions/UsageOfDeprecatedVerify.html b/src/main/resources/inspectionDescriptions/UsageOfDeprecatedVerify.html index 1ab07901..ed6a81f6 100644 --- a/src/main/resources/inspectionDescriptions/UsageOfDeprecatedVerify.html +++ b/src/main/resources/inspectionDescriptions/UsageOfDeprecatedVerify.html @@ -11,10 +11,6 @@
  • Mockito.verifyZeroInteractions(Object...) that can be replaced with Mockito.verifyNoMoreInteractions(Object...)
  • -

    - The inspection is applied only when the Mockito version included on the module classpath of the currently - inspected file is 3.x. -

    MockedStatic.verify()

     
    diff --git a/src/main/resources/inspectionDescriptions/UsageOfOldJUnitRunner.html b/src/main/resources/inspectionDescriptions/UsageOfOldJUnitRunner.html
    index adf81d71..0dbd8ae4 100644
    --- a/src/main/resources/inspectionDescriptions/UsageOfOldJUnitRunner.html
    +++ b/src/main/resources/inspectionDescriptions/UsageOfOldJUnitRunner.html
    @@ -10,9 +10,6 @@
         
  • MockitoJUnitRunner
  • to org.mockito.junit.MockitoJUnitRunner. -

    - The inspection is applied only when the Mockito version included on the module classpath of the currently - inspected file is 2.x or 3.x.

     
     //from:
    diff --git a/src/test/java/com/picimako/mockitools/MockableTypesUtilTest.java b/src/test/java/com/picimako/mockitools/MockableTypesUtilTest.java
    index 6745e370..7a93b4d9 100644
    --- a/src/test/java/com/picimako/mockitools/MockableTypesUtilTest.java
    +++ b/src/test/java/com/picimako/mockitools/MockableTypesUtilTest.java
    @@ -60,6 +60,6 @@ public class IsNotMockableTypeWrapperTest {
         }
     
         private PsiType getFieldType() {
    -        return computeBlocking(() -> ((PsiField) getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent()).getTypeElement().getType());
    +        return computeBlocking(() -> ((PsiField) getFile().findElementAt(getCaretOffset()).getParent()).getTypeElement().getType());
         }
     }
    diff --git a/src/test/java/com/picimako/mockitools/MockitoolsActionTestBase.java b/src/test/java/com/picimako/mockitools/MockitoolsActionTestBase.java
    index 061befdf..e7664213 100644
    --- a/src/test/java/com/picimako/mockitools/MockitoolsActionTestBase.java
    +++ b/src/test/java/com/picimako/mockitools/MockitoolsActionTestBase.java
    @@ -4,7 +4,7 @@
     
     import com.intellij.openapi.actionSystem.AnAction;
     import com.intellij.openapi.application.ApplicationManager;
    -import org.assertj.core.api.Assertions;
    +import org.jetbrains.annotations.NotNull;
     
     import java.util.function.Supplier;
     
    @@ -13,15 +13,13 @@
      */
     public abstract class MockitoolsActionTestBase extends MockitoolsTestBase {
     
    -    protected void checkAction(Supplier action, String beforeText, String afterText) {
    +    /**
    +     * Configures a test file with {@code beforeText}, runs the provided {@code action} and checks
    +     * that the test file's content has changed to {@code afterText}.
    +     */
    +    protected void checkAction(@NotNull Supplier action, @NotNull String beforeText, @NotNull String afterText) {
             getFixture().configureByText("ConversionTest.java", beforeText);
             ApplicationManager.getApplication().invokeAndWait(() -> getFixture().testAction(action.get()));
             getFixture().checkResult(afterText);
         }
    -
    -    protected void checkActionFlexible(Supplier action, String beforeText, String afterText) {
    -        getFixture().configureByText("ConversionTest.java", beforeText);
    -        ApplicationManager.getApplication().invokeAndWait(() -> getFixture().testAction(action.get()));
    -        Assertions.assertThat(getFixture().getEditor().getDocument().getText()).endsWith(afterText);
    -    }
     }
    diff --git a/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilDoNotMockTest.java b/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilDoNotMockTest.java
    index e57d0a96..5fef29bb 100644
    --- a/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilDoNotMockTest.java
    +++ b/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilDoNotMockTest.java
    @@ -182,7 +182,7 @@ public class DoNotMockTest {
         }
     
         private PsiType getFieldType() {
    -        return computeBlocking(() -> ((PsiField) getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent()).getTypeElement().getType());
    +        return computeBlocking(() -> ((PsiField) getFile().findElementAt(getCaretOffset()).getParent()).getTypeElement().getType());
         }
     
         private static void assertHasReason(MockableTypesUtil.DoNotMockType doNotMock, String expected) {
    diff --git a/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilTest.java b/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilTest.java
    index 1e66e8db..240dd507 100644
    --- a/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilTest.java
    +++ b/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilTest.java
    @@ -33,7 +33,7 @@ private Stream specificMethodData() {
                 new TestData("IsMockitoMockTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsMockitoMockTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
    @@ -42,7 +42,7 @@ public void testMethod() {
                 new TestData("IsMockitoSpyTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsMockitoSpyTest {
                             public void testMethod() {
                                 Object mock = Mockito.spy(Object.class);
    @@ -52,7 +52,7 @@ public void testMethod() {
                     """
                         import org.mockito.BDDMockito;
                         import org.mockito.AdditionalMatchers;
    -
    +                    
                         public class IsAdditionalMatchersTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
    @@ -63,7 +63,7 @@ public void testMethod() {
                     """
                         import org.mockito.Mockito;
                         import static org.mockito.Mockito.times;
    -
    +                    
                         public class IsTimesTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
    @@ -74,7 +74,7 @@ public void testMethod() {
                     """
                         import org.mockito.Mockito;
                         import static org.mockito.Mockito.calls;
    -
    +                    
                         public class IsCallsTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
    @@ -85,7 +85,7 @@ public void testMethod() {
                     """
                         import org.mockito.Mockito;
                         import static org.mockito.Mockito.after;
    -
    +                    
                         public class IsAfterTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
    @@ -96,7 +96,7 @@ public void testMethod() {
                     """
                         import org.mockito.Mockito;
                         import static org.mockito.Mockito.timeout;
    -
    +                    
                         public class IsTimeoutTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
    @@ -107,7 +107,7 @@ public void testMethod() {
                     """
                         import org.mockito.Mockito;
                         import java.util.List;
    -
    +                    
                         public class IsExtraInterfacesTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class, Mockito.withSettings().extraInterfaces(List.class));
    @@ -116,7 +116,7 @@ public void testMethod() {
                 new TestData("IsResetTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsResetTest {
                             public void testMethod() {
                                 Mockito.reset(Mockito.mock(Object.class));
    @@ -125,7 +125,7 @@ public void testMethod() {
                 new TestData("IsIgnoreStubsTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsIgnoreStubsTest {
                             public void testMethod() {
                                 Mockito.ignoreStubs(Mockito.mock(Object.class));
    @@ -145,23 +145,23 @@ void testIsNotASpecificMethod(TestData data) {
         private Stream notASpecifiedMethodData() {
             String fileContent = """
                 import org.mockito.Mockito;
    -
    +            
                 public class %s {
                     public void testMethod() {
                         String toString = new Object().toString();
                     }
                 }""";
             return Stream.of(
    -            new TestData("IsNotMockitoMockTest.java", String.format(fileContent, "IsNotMockitoMockTest"), () -> MockitoolsPsiUtil.isMockitoMock(getMethodCall())),
    -            new TestData("IsNotMockitoSpyTest.java", String.format(fileContent, "IsNotMockitoSpyTest"), () -> MockitoolsPsiUtil.isMockitoSpy(getMethodCall())),
    -            new TestData("IsNotAdditionalMatchersTest.java", String.format(fileContent, "IsNotAdditionalMatchersTest"), () -> MockitoolsPsiUtil.isAdditionalMatchers(getMethodCall())),
    -            new TestData("isNotTimesTest.java", String.format(fileContent, "isNotTimesTest"), () -> MockitoolsPsiUtil.isTimes(getMethodCall())),
    -            new TestData("isNotCallsTest.java", String.format(fileContent, "isNotCallsTest"), () -> MockitoolsPsiUtil.isCalls(getMethodCall())),
    -            new TestData("isNotAfterTest.java", String.format(fileContent, "isNotAfterTest"), () -> MockitoolsPsiUtil.isAfter(getMethodCall())),
    -            new TestData("isNotTimeoutTest.java", String.format(fileContent, "isNotTimeoutTest"), () -> MockitoolsPsiUtil.isTimeout(getMethodCall())),
    -            new TestData("isNotExtraInterfacesTest.java", String.format(fileContent, "isNotExtraInterfacesTest"), () -> MockitoolsPsiUtil.isExtraInterfaces(getMethodCall())),
    -            new TestData("isNotResetTest.java", String.format(fileContent, "isNotResetTest"), () -> MockitoolsPsiUtil.isReset(getMethodCall())),
    -            new TestData("isNotIgnoreStunsTest.java", String.format(fileContent, "isNotIgnoreStubsTest"), () -> MockitoolsPsiUtil.isIgnoreStubs(getMethodCall()))
    +            new TestData("IsNotMockitoMockTest.java", fileContent.formatted("IsNotMockitoMockTest"), () -> MockitoolsPsiUtil.isMockitoMock(getMethodCall())),
    +            new TestData("IsNotMockitoSpyTest.java", fileContent.formatted("IsNotMockitoSpyTest"), () -> MockitoolsPsiUtil.isMockitoSpy(getMethodCall())),
    +            new TestData("IsNotAdditionalMatchersTest.java", fileContent.formatted("IsNotAdditionalMatchersTest"), () -> MockitoolsPsiUtil.isAdditionalMatchers(getMethodCall())),
    +            new TestData("isNotTimesTest.java", fileContent.formatted("isNotTimesTest"), () -> MockitoolsPsiUtil.isTimes(getMethodCall())),
    +            new TestData("isNotCallsTest.java", fileContent.formatted("isNotCallsTest"), () -> MockitoolsPsiUtil.isCalls(getMethodCall())),
    +            new TestData("isNotAfterTest.java", fileContent.formatted("isNotAfterTest"), () -> MockitoolsPsiUtil.isAfter(getMethodCall())),
    +            new TestData("isNotTimeoutTest.java", fileContent.formatted("isNotTimeoutTest"), () -> MockitoolsPsiUtil.isTimeout(getMethodCall())),
    +            new TestData("isNotExtraInterfacesTest.java", fileContent.formatted("isNotExtraInterfacesTest"), () -> MockitoolsPsiUtil.isExtraInterfaces(getMethodCall())),
    +            new TestData("isNotResetTest.java", fileContent.formatted("isNotResetTest"), () -> MockitoolsPsiUtil.isReset(getMethodCall())),
    +            new TestData("isNotIgnoreStunsTest.java", fileContent.formatted("isNotIgnoreStubsTest"), () -> MockitoolsPsiUtil.isIgnoreStubs(getMethodCall()))
             );
         }
     
    @@ -178,12 +178,12 @@ private Stream isCalledTheSameButNotTheSpecificMethodData() {
             return Stream.of(
                 new TestData("IsMockNotMockitoMockTest.java",
                     """
    -
    +                    
                         public class IsMockNotMockitoMockTest {
                             public void testMethod() {
                                 Mock.mock(Object.class);
                             }
    -
    +                    
                             private static final class Mock {
                                 public static Mock mock(Object object) {
                                     return new Mock();
    @@ -193,12 +193,12 @@ public static Mock mock(Object object) {
                 new TestData("IsSpyNotMockitoSpyTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsSpyNotMockitoSpyTest {
                             public void testMethod() {
                                 Spy.spy(Object.class);
                             }
    -
    +                    
                             private static final class Spy {
                                 public static Spy spy(Object object) {
                                     return new Spy();
    @@ -208,13 +208,13 @@ public static Spy spy(Object object) {
                 new TestData("IsTimesNotMockitoTimesTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsTimesNotMockitoTimesTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 Mockito.verify(mock, Times.times(1)).toString();
                             }
    -
    +                    
                             private static final class Times {
                                 public static Times times(Object object) {
                                     return new Times();
    @@ -224,13 +224,13 @@ public static Times times(Object object) {
                 new TestData("IsCallsNotMockitoCallsTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsCallsNotMockitoCallsTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 Mockito.verify(mock, Times.calls(1)).toString();
                             }
    -
    +                    
                             private static final class Calls {
                                 public static Calls calls(Object object) {
                                     return new Calls();
    @@ -240,13 +240,13 @@ public static Calls calls(Object object) {
                 new TestData("IsAfterNotMockitoAfterTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsAfterNotMockitoAfterTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 Mockito.verify(mock, Times.after(1)).toString();
                             }
    -
    +                    
                             private static final class After {
                                 public static After after(Object object) {
                                     return new After();
    @@ -256,13 +256,13 @@ public static After after(Object object) {
                 new TestData("IsTimeoutNotMockitoTimeoutTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsTimeoutNotMockitoTimeoutTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 Mockito.verify(mock, Times.timeout(1)).toString();
                             }
    -
    +                    
                             private static final class Timeout {
                                 public static Timeout timeout(Object object) {
                                     return new Timeout();
    @@ -272,13 +272,13 @@ public static Timeout timeout(Object object) {
                 new TestData("IsExtraInterfacesNotMockitoSettingsExtraInterfacesTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsExtraInterfacesNotMockitoSettingsExtraInterfacesTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 Mockito.verify(mock, ExtraInterfaces.extraInterfaces(1)).toString();
                             }
    -
    +                    
                             private static final class ExtraInterfaces {
                                 public static ExtraInterfaces extraInterfaces(Object object) {
                                     return new ExtraInterfaces();
    @@ -288,13 +288,13 @@ public static ExtraInterfaces extraInterfaces(Object object) {
                 new TestData("IsResetNotMockitoResetTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsResetNotMockitoResetTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 Reset.reset(mock);
                             }
    -
    +                    
                             private static final class Reset {
                                 public static Reset reset(Object object) {
                                     return new Reset();
    @@ -304,13 +304,13 @@ public static Reset reset(Object object) {
                 new TestData("IsIgnoreStubsNotMockitoIgnoreStubsTest.java",
                     """
                         import org.mockito.Mockito;
    -
    +                    
                         public class IsIgnoreStubsNotMockitoIgnoreStubsTest {
                             public void testMethod() {
                                 Object mock = Mockito.mock(Object.class);
                                 IgnoreStubs.ignoreStubs(mock);
                             }
    -
    +                    
                             private static final class IgnoreStubs {
                                 public static IgnoreStubs ignoreStubs(Object object) {
                                     return new IgnoreStubs();
    @@ -328,7 +328,7 @@ void testIsArgumentCaptor() {
                 """
                     import org.mockito.ArgumentCaptor;
                     import org.mockito.Captor;
    -
    +                
                     public class IsArgumentCaptorTest {
                         @Captor
                         public ArgumentCaptor captor;
    @@ -344,7 +344,7 @@ void testIsNotArgumentCaptor() {
                 """
                     import org.mockito.ArgumentCaptor;
                     import org.mockito.Captor;
    -
    +                
                     public class IsNotArgumentCaptorTest {
                         @Captor
                         public String captor;
    @@ -361,7 +361,7 @@ void testIsNotMatchers() {
             getFixture().configureByText("isNotMatchersTest.java",
                 """
                     import org.mockito.ArgumentMatchers;
    -
    +                
                     public class isNotMatchersTest {
                         public void testMethod() {
                             ArgumentMatchers.anyString();
    @@ -372,11 +372,11 @@ public void testMethod() {
         }
     
         private PsiMethodCallExpression getMethodCall() {
    -        return (PsiMethodCallExpression) computeBlocking(() -> getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent().getParent());
    +        return (PsiMethodCallExpression) computeBlocking(() -> getFile().findElementAt(getCaretOffset()).getParent().getParent());
         }
     
         private PsiField getField() {
    -        return (PsiField) computeBlocking(() -> getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent());
    +        return (PsiField) computeBlocking(() -> getFile().findElementAt(getCaretOffset()).getParent());
         }
     
         private record TestData(String fileName, String fileContent, Supplier isSpecificMethod) {
    diff --git a/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilV3Test.java b/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilV3Test.java
    index 45ea36f2..8668afb3 100644
    --- a/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilV3Test.java
    +++ b/src/test/java/com/picimako/mockitools/MockitoolsPsiUtilV3Test.java
    @@ -34,6 +34,6 @@ public void testMethod() {
     
     
         private PsiMethodCallExpression getMethodCall() {
    -        return (PsiMethodCallExpression) computeBlocking(() -> getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent().getParent());
    +        return (PsiMethodCallExpression) computeBlocking(() -> getFile().findElementAt(getCaretOffset()).getParent().getParent());
         }
     }
    diff --git a/src/test/java/com/picimako/mockitools/MockitoolsTestBase.java b/src/test/java/com/picimako/mockitools/MockitoolsTestBase.java
    index 0ccc45fd..ea206341 100644
    --- a/src/test/java/com/picimako/mockitools/MockitoolsTestBase.java
    +++ b/src/test/java/com/picimako/mockitools/MockitoolsTestBase.java
    @@ -2,55 +2,105 @@
     
     package com.picimako.mockitools;
     
    +import com.intellij.openapi.editor.Editor;
    +import com.intellij.openapi.project.Project;
     import com.intellij.openapi.projectRoots.JavaSdk;
    +import com.intellij.psi.PsiFile;
     import com.intellij.testFramework.TestDataPath;
     import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
     import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase5;
     import org.jetbrains.annotations.Nullable;
    +import org.jspecify.annotations.NonNull;
     
     /**
    - * Base test class for non-inspection integration tests.
    - * 

    - * Configures the Java JDK and loads the Mockito binary. + * Base test class for Mockitools tests. */ public abstract class MockitoolsTestBase extends LightJavaCodeInsightFixtureTestCase5 { - private static final String BASE_PATH = "src/test/testData/"; - protected MockitoolsTestBase() { - //Returns a descriptor with a real JDK defined by the JAVA_HOME environment variable. - super(new DefaultLightProjectDescriptor(() -> JavaSdk.getInstance().createJdk("Real JDK", System.getenv("JAVA_HOME"), false)) - .withRepositoryLibrary(ThirdPartyLibrary.MOCKITO_V5.getMavenCoordinate())); + super(projectDescriptorWithRealJdk().withRepositoryLibrary(ThirdPartyLibrary.MOCKITO_V5.getMavenCoordinate())); } protected MockitoolsTestBase(ThirdPartyLibrary... libraries) { super(createDescriptor(libraries)); } - private static DefaultLightProjectDescriptor createDescriptor(ThirdPartyLibrary... libraries) { - var descriptor = new DefaultLightProjectDescriptor(() -> JavaSdk.getInstance().createJdk("Real JDK", System.getenv("JAVA_HOME"), false)); + /** + * Returns a descriptor with a real JDK defined by the {@code JAVA_HOME} environment variable. + */ + private static @NonNull DefaultLightProjectDescriptor projectDescriptorWithRealJdk() { + return new DefaultLightProjectDescriptor(() -> JavaSdk.getInstance().createJdk("Real JDK", System.getenv("JAVA_HOME"), false)); + } + + /** + * Returns a project descriptor that has the provided third-party {@code libraries} configured + * as dependencies for the test project. + */ + private static @NonNull DefaultLightProjectDescriptor createDescriptor(ThirdPartyLibrary... libraries) { + var descriptor = projectDescriptorWithRealJdk(); for (var library : libraries) { descriptor.withRepositoryLibrary(library.getMavenCoordinate(), false); } return descriptor; } + /** + * Analyzes the class hierarchy of the current test class and finds the {@link TestDataPath} annotation + * on one of the classes in it. It stops at {@link LightJavaCodeInsightFixtureTestCase5} because it is certain + * that it has the annotation specified. Then, it returns the value of the annotation. + *

    + * By using the {@link TestDataPath} annotation on test classes, overriding this method in every test class + * is no longer needed. It provides a more declarative approach. + */ @Nullable @Override protected String getTestDataPath() { - var testDataPathOnCurrentClass = getClass().getAnnotation(TestDataPath.class); + var testDataPathOnCurrentClass = testDataPathOn(getClass()); //If the current test class is annotated as TestDataPath, return its value - if (testDataPathOnCurrentClass != null) - return testDataPathOnCurrentClass.value().replace("$CONTENT_ROOT/testData/", BASE_PATH); + if (testDataPathOnCurrentClass != null) return replaceContentRoot(testDataPathOnCurrentClass); //If the current test class is not annotated as TestDataPath, find the first class in its class hierarchy // that is annotated. It stops looking when reaching LightJavaCodeInsightFixtureTestCase. var clazz = getClass().getSuperclass(); - while (!clazz.equals(LightJavaCodeInsightFixtureTestCase5.class) && clazz.getAnnotation(TestDataPath.class) == null) { + while (!clazz.equals(LightJavaCodeInsightFixtureTestCase5.class) && testDataPathOn(clazz) == null) { clazz = clazz.getSuperclass(); } - return clazz.getAnnotation(TestDataPath.class).value().replace("$CONTENT_ROOT/testData/", BASE_PATH); + return replaceContentRoot(testDataPathOn(clazz)); + } + + /** + * Returns a normalized version of the annotation's value by replacing the beginning of it with a path + * referencing the test data directory in this project. + *

    + * By starting the {@link TestDataPath} annotation value with {@code $CONTENT_ROOT}, it enables path reference + * fragments injection into that path by the IDE, but has to be replaced with the actual relative path, so that it is + * resolved by the parent test case. + */ + private static String replaceContentRoot(TestDataPath testDataPath) { + return testDataPath.value().replace("$CONTENT_ROOT/testData/", "src/test/testData/"); + } + + private static TestDataPath testDataPathOn(Class clazz) { + return clazz.getAnnotation(TestDataPath.class); + } + + //Helpers + + protected Project getProject() { + return getFixture().getProject(); + } + + protected Editor getEditor() { + return getFixture().getEditor(); + } + + protected PsiFile getFile() { + return getFixture().getFile(); + } + + protected int getCaretOffset() { + return getFixture().getCaretOffset(); } } diff --git a/src/test/java/com/picimako/mockitools/ThirdPartyLibrary.java b/src/test/java/com/picimako/mockitools/ThirdPartyLibrary.java index 48ee937d..a33c9434 100644 --- a/src/test/java/com/picimako/mockitools/ThirdPartyLibrary.java +++ b/src/test/java/com/picimako/mockitools/ThirdPartyLibrary.java @@ -11,7 +11,6 @@ @RequiredArgsConstructor public enum ThirdPartyLibrary { MOCKITO_V3("org.mockito:mockito-core:3.12.4"), - MOCKITO_V4("org.mockito:mockito-core:4.11.0"), MOCKITO_V5("org.mockito:mockito-core:5.23.0"), JUNIT_4("junit:junit:4.13.2"); diff --git a/src/test/java/com/picimako/mockitools/completion/MockCompletionContributorTest.java b/src/test/java/com/picimako/mockitools/completion/MockCompletionContributorTest.java index 13c9595d..0dc8efdf 100644 --- a/src/test/java/com/picimako/mockitools/completion/MockCompletionContributorTest.java +++ b/src/test/java/com/picimako/mockitools/completion/MockCompletionContributorTest.java @@ -2,6 +2,7 @@ package com.picimako.mockitools.completion; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; /** @@ -145,7 +146,7 @@ void shouldCompleteMiddleArgument() { "mock", "spy", "someInjectMocks", "someMock", "someSpy"); } - private void testCompletionContains(String importStatement, String mockContainerCall, String... completionItems) { + private void testCompletionContains(@NotNull String importStatement, @NotNull String mockContainerCall, @NotNull String... completionItems) { doTestCodeCompletionContains(COMPLETION_TEST_CLASS.formatted(importStatement, mockContainerCall), completionItems); } } diff --git a/src/test/java/com/picimako/mockitools/completion/MockitoolsCodeCompletionTestBase.java b/src/test/java/com/picimako/mockitools/completion/MockitoolsCodeCompletionTestBase.java index 761a4c75..bacbecaf 100644 --- a/src/test/java/com/picimako/mockitools/completion/MockitoolsCodeCompletionTestBase.java +++ b/src/test/java/com/picimako/mockitools/completion/MockitoolsCodeCompletionTestBase.java @@ -5,6 +5,7 @@ import com.intellij.codeInsight.completion.CompletionType; import com.picimako.mockitools.MockitoolsTestBase; +import org.jetbrains.annotations.NotNull; /** * Base class for testing code completion. @@ -12,13 +13,10 @@ public abstract class MockitoolsCodeCompletionTestBase extends MockitoolsTestBase { /** - * Tests basic code completion in the provided file text at the marked caret position, - * and validates if the completion contains exactly the expected items. - * - * @param text the file text on which the code completion is invoked - * @param completionItems the list of items that are expected to be in the displayed completion list + * Configures a file with the provided {@code text}, test basic code completion at the marked caret position, + * and validates if the completion contains exactly the expected {@code completionItems}. */ - protected void doTestCodeCompletionContains(String text, String... completionItems) { + protected void doTestCodeCompletionContains(@NotNull String text, @NotNull String... completionItems) { assertThat(text).withFailMessage("File text must contain to invoke code completion.").contains(""); getFixture().configureByText("CompletionTest.java", text); diff --git a/src/test/java/com/picimako/mockitools/inspection/CallOnMockitoResetInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/CallOnMockitoResetInspectionTest.java index 09d4aed3..1494d534 100644 --- a/src/test/java/com/picimako/mockitools/inspection/CallOnMockitoResetInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/CallOnMockitoResetInspectionTest.java @@ -8,7 +8,7 @@ /** * Functional test for {@link CallOnMockitoResetInspection} */ -class CallOnMockitoResetInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class CallOnMockitoResetInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/MockitoolsInspectionTestBase.java b/src/test/java/com/picimako/mockitools/inspection/MockitoolsInspectionTestBase.java index 97c4cbd1..0c2221bd 100644 --- a/src/test/java/com/picimako/mockitools/inspection/MockitoolsInspectionTestBase.java +++ b/src/test/java/com/picimako/mockitools/inspection/MockitoolsInspectionTestBase.java @@ -8,9 +8,7 @@ import com.picimako.mockitools.ThirdPartyLibrary; /** - * Base test class for Mockitools inspection unit testing. - *

    - * Loads the Java 11 mock JDK and the Mockito binary for testing. + * Base test class for inspection testing. */ @TestDataPath("$CONTENT_ROOT/testData/inspection") public abstract class MockitoolsInspectionTestBase extends MockitoolsTestBase { @@ -24,35 +22,57 @@ public MockitoolsInspectionTestBase(ThirdPartyLibrary... libraries) { } /** - * Override this to configure the inspection to be tested. + * Returns the inspection instance that is the subject of this test. */ protected abstract InspectionProfileEntry getInspection(); + //Highlighting + + /** + * Configures a Java test file + *

      + *
    • with the name of the current test method without the {@code test} prefix
    • + *
    • and the contents of the test data file with that name
    • + *
    + * and checks highlighting for the subject inspection without checking info-level highlights. + */ protected void doJavaTest() { doJavaTest(getInspection(), false); } /** - * Tests highlighting for the pre-configured inspection against the java file matching the test method's name - * without the 'test' prefix. + * Configures a Java test file + *
      + *
    • with the name of the current test method without the {@code test} prefix
    • + *
    • and the contents of the test data file with that name
    • + *
    + * and checks highlighting for the subject inspection. + * Info-level highlights are included based on the {@code checkInfos} flag. */ protected void doJavaTest(boolean checkInfos) { doJavaTest(getInspection(), checkInfos); } /** - * Tests highlighting for the argument inspection against the java file matching the test method's name - * without the 'test' prefix. + * Configures a Java test file + *
      + *
    • with the name of the current test method without the {@code test} prefix
    • + *
    • and the contents of the test data file with that name
    • + *
    + * and checks highlighting for the provided inspection without checking info-level highlights. */ protected void doJavaTest(InspectionProfileEntry inspection) { - getFixture().configureByFile(getTestName(false) + ".java"); - getFixture().enableInspections(inspection); - getFixture().testHighlighting(true, false, false); + doJavaTest(inspection, false); } /** - * Tests highlighting for the argument inspection against the java file matching the test method's name - * without the 'test' prefix. + * Configures a Java test file + *
      + *
    • with the name of the current test method without the {@code test} prefix
    • + *
    • and the contents of the test data file with that name
    • + *
    + * and checks highlighting for the provided inspection. + * Info-level highlights are included based on the {@code checkInfos} flag. */ protected void doJavaTest(InspectionProfileEntry inspection, boolean checkInfos) { getFixture().configureByFile(getTestName(false) + ".java"); @@ -60,18 +80,26 @@ protected void doJavaTest(InspectionProfileEntry inspection, boolean checkInfos) getFixture().testHighlighting(true, checkInfos, true); } + /** + * Configures a Java test file with the given {@code filename} and {@code text} + * and checks highlighting for the subject inspection. + */ protected void doJavaTest(String filename, String text) { getFixture().configureByText(filename, text); getFixture().enableInspections(getInspection()); getFixture().testHighlighting(true, false, false); } + //Quick fixes + /** - * Tests highlighting and quick fix for the pre-configured inspection, applying the argument quick fix against the - * java file matching the test method's name without the 'test' prefix (e.g. SomeTest.java) as the before state, - * and e.g. SomeTest.after.java as the expected state after applying the quick fix. - * - * @param quickFixName the name/text of the quick fix + * Configures a Java test file (the before state) + *
      + *
    • with the name of the current test method without the {@code test} prefix
    • + *
    • and the contents of the test data file with that name
    • + *
    + * launches the quick of the provided name and checks if the file text has changed to the after state + * provided similarly via a corresponding {@code [testname].after.java} file. */ protected void doQuickFixTest(String quickFixName) { getFixture().configureByFile(getTestName(false) + ".java"); @@ -80,20 +108,27 @@ protected void doQuickFixTest(String quickFixName) { } /** - * Tests highlighting and quick fix for the pre-configured inspection, applying the argument quick fix against the - * provided beforeText, and the after state against the argument afterText. - * - * @param quickFixName the name/text of the quick fix - * @param filename the filename in which the before text will be configured - * @param beforeText the code before applying the quick fix - * @param afterText the code after applying the quick fix + * Configures a test file with the provided {@code filename} and {@code beforeText}, + * launches the quick of the provided {@code quickFixName} and checks if the file text has changed to the after state + * provided in {@code afterText}. */ protected void doQuickFixTest(String quickFixName, String filename, String beforeText, String afterText) { getFixture().configureByText(filename, beforeText); launchQuickFix(quickFixName); getFixture().checkResult(afterText); } - + + /** + * Configures a test file with a fix name and {@code beforeText}, + * launches the quick of the provided {@code quickFixName} and checks if the file text has changed to the after state + * provided in {@code afterText}. + */ + protected void doQuickFixTest(String quickFixName, String beforeText, String afterText) { + getFixture().configureByText("QuickFix.java", beforeText); + launchQuickFix(quickFixName); + getFixture().checkResult(afterText); + } + private void launchQuickFix(String quickFixName) { getFixture().enableInspections(getInspection()); getFixture().doHighlighting(); @@ -112,13 +147,4 @@ public MockitoV3(ThirdPartyLibrary... libraries) { super(libraries); } } - - /** - * Base class for testing Mockito 4 specific inspections. - */ - public static abstract class MockitoV4 extends MockitoolsInspectionTestBase { - public MockitoV4() { - super(ThirdPartyLibrary.MOCKITO_V4); - } - } } diff --git a/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspectionTest.java index a76abc9f..e9da46bf 100644 --- a/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldInitializationInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link CaptorFieldInitializationInspection}. */ -class CaptorFieldInitializationInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class CaptorFieldInitializationInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspectionTest.java index bc712511..78fa920a 100644 --- a/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/captor/CaptorFieldOfTypeArgumentCaptorInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link CaptorFieldOfTypeArgumentCaptorInspection}. */ -class CaptorFieldOfTypeArgumentCaptorInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class CaptorFieldOfTypeArgumentCaptorInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { @@ -23,12 +23,11 @@ void testCaptorFieldOfType() { @Test void testCaptorFieldConvertToArgumentCaptorArrayTest() { - doQuickFixTest("Convert field type to ArgumentCaptor<>", "CaptorFieldConvertToArgumentCaptorArrayTest.java", + doQuickFixTest( """ import org.mockito.Captor; - public class CaptorFieldConvertToArgumentCaptorArrayTest { - + public class CaptorFieldConvert { @Captor public String[] captor; } @@ -37,8 +36,7 @@ public class CaptorFieldConvertToArgumentCaptorArrayTest { import org.mockito.ArgumentCaptor; import org.mockito.Captor; - public class CaptorFieldConvertToArgumentCaptorArrayTest { - + public class CaptorFieldConvert { @Captor public ArgumentCaptor captor; } @@ -47,12 +45,11 @@ public class CaptorFieldConvertToArgumentCaptorArrayTest { @Test void testCaptorFieldConvertToArgumentCaptorPrimitiveTest() { - doQuickFixTest("Convert field type to ArgumentCaptor<>", "CaptorFieldConvertToArgumentCaptorPrimitiveTest.java", + doQuickFixTest( """ import org.mockito.Captor; - public class CaptorFieldConvertToArgumentCaptorPrimitiveTest { - + public class CaptorFieldConvert { @Captor public char captor; } @@ -61,8 +58,7 @@ public class CaptorFieldConvertToArgumentCaptorPrimitiveTest { import org.mockito.ArgumentCaptor; import org.mockito.Captor; - public class CaptorFieldConvertToArgumentCaptorPrimitiveTest { - + public class CaptorFieldConvert { @Captor public ArgumentCaptor captor; } @@ -71,13 +67,12 @@ public class CaptorFieldConvertToArgumentCaptorPrimitiveTest { @Test void testCaptorFieldConvertToArgumentCaptorGenericsTest() { - doQuickFixTest("Convert field type to ArgumentCaptor<>", "CaptorFieldConvertToArgumentCaptorGenericsTest.java", + doQuickFixTest( """ import org.mockito.Captor; import java.util.List; - public class CaptorFieldConvertToArgumentCaptorGenericsTest { - + public class CaptorFieldConvert { @Captor public List> captor; } @@ -87,8 +82,7 @@ public class CaptorFieldConvertToArgumentCaptorGenericsTest { import org.mockito.Captor; import java.util.List; - public class CaptorFieldConvertToArgumentCaptorGenericsTest { - + public class CaptorFieldConvert { @Captor public ArgumentCaptor>> captor; } @@ -97,14 +91,13 @@ public class CaptorFieldConvertToArgumentCaptorGenericsTest { @Test void testCaptorFieldConvertToArgumentCaptorWildcardGenericsTest() { - doQuickFixTest("Convert field type to ArgumentCaptor<>", "CaptorFieldConvertToArgumentCaptorWildcardGenericsTest.java", + doQuickFixTest( """ import org.mockito.Captor; import java.util.List; import java.util.Map; - public class CaptorFieldConvertToArgumentCaptorWildcardGenericsTest { - + public class CaptorFieldConvert { @Captor public Map, List> captor; } @@ -115,11 +108,14 @@ public class CaptorFieldConvertToArgumentCaptorWildcardGenericsTest { import java.util.List; import java.util.Map; - public class CaptorFieldConvertToArgumentCaptorWildcardGenericsTest { - + public class CaptorFieldConvert { @Captor public ArgumentCaptor, List>> captor; } """); } + + private void doQuickFixTest(String before, String after) { + doQuickFixTest("Convert field type to ArgumentCaptor<>", "CaptorFieldConvert.java", before, after); + } } diff --git a/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveReturnCallsInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveReturnCallsInspectionTest.java index 43c2059b..a0cd3bf5 100644 --- a/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveReturnCallsInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveReturnCallsInspectionTest.java @@ -16,7 +16,7 @@ /** * Functional test for {@link SimplifyConsecutiveReturnCallsInspection}. */ -class SimplifyConsecutiveReturnCallsInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class SimplifyConsecutiveReturnCallsInspectionTest extends MockitoolsInspectionTestBase { private static final String MOCK_OBJECT_CLASS = """ @@ -436,70 +436,73 @@ protected InspectionProfileEntry getInspection() { return new SimplifyConsecutiveReturnCallsInspection(); } -//Highlighting + //Highlighting @Test void testSimplifyConsecutiveStubbingCalls() { doJavaTest(); } -//Quick fixes + //Quick fixes @ParameterizedTest @MethodSource("whenThenReturnCases") void testReplacesWhenThenReturns(String before, String after) { - doQuickFixTest("Merge thenReturn calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge thenReturn calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("doReturnWhenCases") void testReplacesDoReturnWhens(String before, String after) { - doQuickFixTest("Merge doReturn calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge doReturn calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("givenWillReturnCases") void testReplacesGivenWillReturns(String before, String after) { - doQuickFixTest("Merge willReturn calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge willReturn calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("willReturnGivenCases") void testReplacesWillReturnGivens(String before, String after) { - doQuickFixTest("Merge willReturn calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge willReturn calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("mockedStaticWhenThenReturnCases") void testReplacesMockedStaticWhenThenReturns(String before, String after) { - doQuickFixTest("Merge thenReturn calls", "QuickFix.java", createMockedStaticClassText(before), createMockedStaticClassText(after)); + doQuickFixTest("Merge thenReturn calls", mockedStaticClassText(before), mockedStaticClassText(after)); } - private String createClassText(String beforeOrAfter) { - return "import org.mockito.Mockito;\n" + - "import org.mockito.BDDMockito;\n" + - "\n" + - "class QuickFix {\n" + - " void testMethod() {\n" + - beforeOrAfter + "\n" + - " }\n" + - "\n" + - MOCK_OBJECT_CLASS + - "}"; + private String classText(String beforeOrAfter) { + return """ + import org.mockito.Mockito; + import org.mockito.BDDMockito; + + class QuickFix { + void testMethod() { + %s + } + %s + } + """.formatted(beforeOrAfter, MOCK_OBJECT_CLASS); } - private String createMockedStaticClassText(String beforeOrAfter) { - return "import org.mockito.Mockito;\n" + - "import org.mockito.MockedStatic;\n" + - "import java.util.Collections;\n" + - "import java.util.List;\n" + - "\n" + - "class QuickFix {\n" + - " void testMethod() {\n" + - " try (MockedStatic mock = Mockito.mockStatic(List.class)) {\n" + - beforeOrAfter + "\n" + - " }\n" + - " }\n" + - "}"; + private String mockedStaticClassText(String beforeOrAfter) { + return """ + import org.mockito.Mockito; + import org.mockito.MockedStatic; + import java.util.Collections; + import java.util.List; + + class QuickFix { + void testMethod() { + try (MockedStatic mock = Mockito.mockStatic(List.class)) { + %s + } + } + } + """.formatted(beforeOrAfter); } } diff --git a/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspectionTest.java index ea433859..2ff0a4cc 100644 --- a/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/consecutive/SimplifyConsecutiveThrowCallsInspectionTest.java @@ -16,7 +16,7 @@ /** * Functional test for {@link SimplifyConsecutiveThrowCallsInspection}; */ -class SimplifyConsecutiveThrowCallsInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class SimplifyConsecutiveThrowCallsInspectionTest extends MockitoolsInspectionTestBase { private static final String MOCK_OBJECT_CLASS = """ @@ -520,73 +520,76 @@ void testSimplifyConsecutiveThrowCalls() { @ParameterizedTest @MethodSource("whenThenThrowCases") void testReplacesWhenThenThrows(String before, String after) { - doQuickFixTest("Merge thenThrow calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge thenThrow calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("doThrowWhenCases") void testReplacesDoThrowWhens(String before, String after) { - doQuickFixTest("Merge doThrow calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge doThrow calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("givenWillThrowCases") void testReplacesGivenWillThrows(String before, String after) { - doQuickFixTest("Merge willThrow calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge willThrow calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("willThrowGivenCases") void testReplacesWillThrowGivens(String before, String after) { - doQuickFixTest("Merge willThrow calls", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge willThrow calls", classText(before), classText(after)); } @ParameterizedTest @MethodSource("toClassesMixedCases") void testMixedToClassesCases(String before, String after) { - doQuickFixTest("Merge calls, convert parameters to Class objects", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge calls, convert parameters to Class objects", classText(before), classText(after)); } @ParameterizedTest @MethodSource("toThrowableMixedCases") void testMixedToThrowablesCases(String before, String after) { - doQuickFixTest("Merge calls, convert parameters to Throwables", "QuickFix.java", createClassText(before), createClassText(after)); + doQuickFixTest("Merge calls, convert parameters to Throwables", classText(before), classText(after)); } @ParameterizedTest @MethodSource("mockedStaticWhenThenThrowsCases") void testReplacesMockedStaticWhenThenThrows(String before, String after) { - doQuickFixTest("Merge thenThrow calls", "QuickFix.java", createMockedStaticClassText(before), createMockedStaticClassText(after)); + doQuickFixTest("Merge thenThrow calls", mockedStaticClassText(before), mockedStaticClassText(after)); } - private String createClassText(String beforeOrAfter) { - return "import org.mockito.Mockito;\n" + - "import org.mockito.BDDMockito;\n" + - "import java.io.IOException;\n" + - "\n" + - "class QuickFix {\n" + - " void testMethod() {\n" + - " MockObject mockObject = Mockito.mock(MockObject.class);\n" + - beforeOrAfter + "\n" + - " }\n" + - "\n" + - MOCK_OBJECT_CLASS + - "}"; + private String classText(String beforeOrAfter) { + return """ + import org.mockito.Mockito; + import org.mockito.BDDMockito; + import java.io.IOException; + + class QuickFix { + void testMethod() { + MockObject mockObject = Mockito.mock(MockObject.class); + %s + } + %s + } + """.formatted(beforeOrAfter, MOCK_OBJECT_CLASS); } - private String createMockedStaticClassText(String beforeOrAfter) { - return "import org.mockito.Mockito;\n" + - "import org.mockito.MockedStatic;\n" + - "import java.util.Collections;\n" + - "import java.util.List;\n" + - "import java.io.IOException;\n" + - "\n" + - "class QuickFix {\n" + - " void testMethod() {\n" + - " try (MockedStatic mock = Mockito.mockStatic(List.class)) {\n" + - beforeOrAfter + "\n" + - " }\n" + - " }\n" + - "}"; + private String mockedStaticClassText(String beforeOrAfter) { + return """ + import org.mockito.Mockito; + import org.mockito.MockedStatic; + import java.util.Collections; + import java.util.List; + import java.io.IOException; + + class QuickFix { + void testMethod() { + try (MockedStatic mock = Mockito.mockStatic(List.class)) { + %s + } + } + } + """.formatted(beforeOrAfter); } } diff --git a/src/test/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspectionTest.java index 123bcb4c..2153f741 100644 --- a/src/test/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/framework/NotExtensibleClassInspectionTest.java @@ -11,7 +11,7 @@ * Functional test for {@link NotExtensibleClassInspection}. */ @TestDataPath("$CONTENT_ROOT/testData/framework/inspection") -class NotExtensibleClassInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class NotExtensibleClassInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/mocking/MockTypeInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/mocking/MockTypeInspectionTest.java index 7fe0b501..e1288e6f 100644 --- a/src/test/java/com/picimako/mockitools/inspection/mocking/MockTypeInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/mocking/MockTypeInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link MockTypeInspection}. */ -class MockTypeInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class MockTypeInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionBDDMockitoV3Test.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionBDDMockitoV3Test.java index 9160e4c2..a9e69725 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionBDDMockitoV3Test.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionBDDMockitoV3Test.java @@ -3,7 +3,7 @@ package com.picimako.mockitools.inspection.stubbing; import com.intellij.codeInspection.InspectionProfileEntry; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.inspection.MockitoolsInspectionTestBase; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionMockitoTest.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionMockitoTest.java index 3d7ddfa8..ded2c11b 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionMockitoTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/EnforceConventionInspectionMockitoTest.java @@ -3,14 +3,14 @@ package com.picimako.mockitools.inspection.stubbing; import com.intellij.codeInspection.InspectionProfileEntry; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.inspection.MockitoolsInspectionTestBase; import org.junit.jupiter.api.Test; /** * Functional test for {@link EnforceConventionInspection}. */ -class EnforceConventionInspectionMockitoTest extends MockitoolsInspectionTestBase.MockitoV4 { +class EnforceConventionInspectionMockitoTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspectionTest.java index 5b6f847a..c48744e8 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/ExtraInterfacesInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link ExtraInterfacesInspection}. */ -class ExtraInterfacesInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class ExtraInterfacesInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspectionTest.java index f0c10277..5d601488 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/InconsistentArgumentMatcherUsageInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link InconsistentArgumentMatcherUsageInspection}. */ -class InconsistentArgumentMatcherUsageInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class InconsistentArgumentMatcherUsageInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionMockitoV3Test.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionMockitoV3Test.java new file mode 100644 index 00000000..d653e3cb --- /dev/null +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionMockitoV3Test.java @@ -0,0 +1,23 @@ +//Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package com.picimako.mockitools.inspection.stubbing; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.picimako.mockitools.inspection.MockitoolsInspectionTestBase; +import org.junit.jupiter.api.Test; + +/** + * Functional test for {@link NoMethodCallArgumentSpecifiedInspection}. + */ +class NoMethodCallArgumentSpecifiedInspectionMockitoV3Test extends MockitoolsInspectionTestBase.MockitoV3 { + + @Override + protected InspectionProfileEntry getInspection() { + return new NoMethodCallArgumentSpecifiedInspection(); + } + + @Test + void testNoMethodCallArgumentMockitoV3Test() { + doJavaTest(); + } +} diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionTest.java index 84689c72..042a9286 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/NoMethodCallArgumentSpecifiedInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link NoMethodCallArgumentSpecifiedInspection}. */ -class NoMethodCallArgumentSpecifiedInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class NoMethodCallArgumentSpecifiedInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspectionTest.java index 7bc34229..b808a2d8 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/StubbingAndMethodReturnTypeMismatchInspectionTest.java @@ -9,7 +9,7 @@ /** * Integration test for {@link StubbingAndMethodReturnTypeMismatchInspection}. */ -class StubbingAndMethodReturnTypeMismatchInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class StubbingAndMethodReturnTypeMismatchInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspectionTest.java index 0d2a8ce7..24c10ef7 100644 --- a/src/test/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/stubbing/ThrowsCheckedExceptionStubbingInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link ThrowsCheckedExceptionStubbingInspection}. */ -class ThrowsCheckedExceptionStubbingInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class ThrowsCheckedExceptionStubbingInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspectionTest.java index 80d10014..15abd781 100644 --- a/src/test/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/verification/SingleInOrderVerificationInspectionTest.java @@ -9,7 +9,7 @@ /** * Integration test for {@link SingleInOrderVerificationInspection}; */ -class SingleInOrderVerificationInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class SingleInOrderVerificationInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspectionTest.java index 1b102378..5d8ea8bf 100644 --- a/src/test/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/verification/StubOnlyMockInVerificationInspectionTest.java @@ -9,7 +9,7 @@ /** * Integration test for {@link StubOnlyMockInVerificationInspection}. */ -class StubOnlyMockInVerificationInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class StubOnlyMockInVerificationInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspectionTest.java index a9d641ae..1a8e8d43 100644 --- a/src/test/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/verification/TimesVerificationModeInspectionTest.java @@ -10,7 +10,7 @@ /** * Functional test for {@link TimesVerificationModeInspection}. */ -class TimesVerificationModeInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class TimesVerificationModeInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspectionTest.java index 425118f8..52f54fe9 100644 --- a/src/test/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/verification/UnusedOrUnconfiguredMockInInOrderVerificationInspectionTest.java @@ -9,7 +9,7 @@ /** * Integration test for {@link UnusedOrUnconfiguredMockInInOrderVerificationInspection}. */ -class UnusedOrUnconfiguredMockInInOrderVerificationInspectionTest extends MockitoolsInspectionTestBase.MockitoV4 { +class UnusedOrUnconfiguredMockInInOrderVerificationInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspectionTest.java b/src/test/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspectionTest.java index cdbe70f6..726a6d3f 100644 --- a/src/test/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspectionTest.java +++ b/src/test/java/com/picimako/mockitools/inspection/verification/VerificationModeValuesBetweenLimitsInspectionTest.java @@ -9,7 +9,7 @@ /** * Functional test for {@link VerificationModeValuesBetweenLimitsInspection}. */ -class VerificationModeValuesBetweenLimitsInspectionTest extends MockitoolsInspectionTestBase.MockitoV3 { +class VerificationModeValuesBetweenLimitsInspectionTest extends MockitoolsInspectionTestBase { @Override protected InspectionProfileEntry getInspection() { diff --git a/src/test/java/com/picimako/mockitools/intention/MockitoolsIntentionTestBase.java b/src/test/java/com/picimako/mockitools/intention/MockitoolsIntentionTestBase.java index c7c547c8..58bdd595 100644 --- a/src/test/java/com/picimako/mockitools/intention/MockitoolsIntentionTestBase.java +++ b/src/test/java/com/picimako/mockitools/intention/MockitoolsIntentionTestBase.java @@ -1,56 +1,83 @@ +//Copyright 2026 Tamás Balog. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + package com.picimako.mockitools.intention; import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.psi.PsiFile; import com.picimako.mockitools.MockitoolsTestBase; +import org.jetbrains.annotations.NotNull; /** * Base class for testing intention actions. */ public abstract class MockitoolsIntentionTestBase extends MockitoolsTestBase { + /** + * Returns the intention action instance that is the subject of this test. + */ protected abstract IntentionAction getIntention(); - protected void checkIntentionIsAvailable(String text) { - PsiFile psiFile = getFixture().configureByText("Available.java", text); + //Availability + + /** + * Configures a file with the provided {@code text}, and checks that the subject intention action + * IS available in the context set up in it. + */ + protected void checkIntentionIsAvailable(@NotNull String text) { + var psiFile = getFixture().configureByText("Available.java", text); - assertThat(getIntention().isAvailable(getFixture().getProject(), getFixture().getEditor(), psiFile)) + assertThat(getIntention().isAvailable(getProject(), getEditor(), psiFile)) .withFailMessage(() -> "Intention is NOT available while it IS supposed to be.") .isTrue(); } - protected void checkIntentionIsNotAvailable(String filename, String text) { - PsiFile psiFile = getFixture().configureByText(filename, text); + /** + * Configures a file with the provided {@code filename} and {@code text}, and checks that the subject intention action + * IS NOT available in the context set up in it. + */ + protected void checkIntentionIsNotAvailable(@NotNull String filename, @NotNull String text) { + var psiFile = getFixture().configureByText(filename, text); - assertThat(getIntention().isAvailable(getFixture().getProject(), getFixture().getEditor(), psiFile)) + assertThat(getIntention().isAvailable(getProject(), getEditor(), psiFile)) .withFailMessage(() -> "Intention IS available while it is NOT supposed to be.") .isFalse(); } - protected void checkIntentionIsNotAvailable(String text) { - PsiFile psiFile = getFixture().configureByText("NotAvailable.java", text); - - assertThat(getIntention().isAvailable(getFixture().getProject(), getFixture().getEditor(), psiFile)) - .withFailMessage(() -> "Intention IS available while it is NOT supposed to be.") - .isFalse(); + /** + * Configures a file with the provided {@code filename} and {@code text}, and checks that the subject intention action + * IS NOT available in the context set up in it. + *

    + * Same as {@link #checkIntentionIsNotAvailable(String, String)} but the filename is fixed. + */ + protected void checkIntentionIsNotAvailable(@NotNull String text) { + checkIntentionIsNotAvailable("NotAvailable.java", text); } - protected void checkIntentionRun(String beforeText, String afterText) { - PsiFile psiFile = getFixture().configureByText("ConversionTest.java", beforeText); - getIntention().invoke(getFixture().getProject(), getFixture().getEditor(), psiFile); + //Execution + + /** + * Configures a file with the provided {@code beforeText}, executes the intention action, and checks + * that the test file's content has changed to {@code afterText}. + */ + protected void checkIntentionRun(@NotNull String beforeText, @NotNull String afterText) { + var psiFile = getFixture().configureByText("ConversionTest.java", beforeText); + getIntention().invoke(getProject(), getEditor(), psiFile); getFixture().checkResult(afterText); } /** - * Invokes the intention on EDT when e.g. a list popup is displayed during the intention actions behaviour - * that requires that thread as a caller. + * Configures a file with the provided {@code beforeText}, executes the intention action, and checks + * that the test file's content has changed to {@code afterText}. + *

    + * Essentially the same as {@link #checkIntentionRun(String, String)}, but this one invokes the intention on EDT + * because there are cases when e.g. a list popup is displayed during the intention action's behaviour that requires + * EDT as a caller. */ - protected void checkIntentionRunOnEdt(String beforeText, String afterText) { - PsiFile psiFile = getFixture().configureByText("ConversionTest.java", beforeText); - ApplicationManager.getApplication().invokeAndWait(() -> getIntention().invoke(getFixture().getProject(), getFixture().getEditor(), psiFile)); + protected void checkIntentionRunOnEdt(@NotNull String beforeText, @NotNull String afterText) { + var psiFile = getFixture().configureByText("ConversionTest.java", beforeText); + ApplicationManager.getApplication().invokeAndWait(() -> getIntention().invoke(getProject(), getEditor(), psiFile)); getFixture().checkResult(afterText); } } diff --git a/src/test/java/com/picimako/mockitools/intention/convert/EnforceConventionAwareIntentionTestBase.java b/src/test/java/com/picimako/mockitools/intention/convert/EnforceConventionAwareIntentionTestBase.java index f91d048d..22ac835c 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/EnforceConventionAwareIntentionTestBase.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/EnforceConventionAwareIntentionTestBase.java @@ -6,8 +6,7 @@ import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.testFramework.InspectionsKt; -import com.picimako.mockitools.Convention; -import com.picimako.mockitools.EnforceConventionUtil; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.inspection.stubbing.EnforceConventionInspection; import com.picimako.mockitools.intention.MockitoolsIntentionTestBase; @@ -17,11 +16,11 @@ public abstract class EnforceConventionAwareIntentionTestBase extends MockitoolsIntentionTestBase { protected void addEnforceConventionInspection(Convention convention) { - var profile = InspectionProfileManager.getInstance(getFixture().getProject()).getCurrentProfile(); + var profile = InspectionProfileManager.getInstance(getProject()).getCurrentProfile(); var inspection = new EnforceConventionInspection(); inspection.conventionToEnforce = convention; InspectionsKt.disableAllTools(profile); - profile.addTool(getFixture().getProject(), new LocalInspectionToolWrapper(inspection), null); - profile.enableTool(EnforceConventionUtil.ENFORCE_CONVENTION_INSPECTION_SHORT_NAME, getFixture().getProject()); + profile.addTool(getProject(), new LocalInspectionToolWrapper(inspection), null); + profile.enableTool(EnforceConventionUtil.ENFORCE_CONVENTION_INSPECTION_SHORT_NAME, getProject()); } } diff --git a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntentionTest.java index 698dacf6..b8342f81 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoGivenIntentionTest.java @@ -2,12 +2,11 @@ package com.picimako.mockitools.intention.convert.stub; -import static java.util.stream.Collectors.toList; +import static com.intellij.util.containers.ContainerUtil.map; import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -281,11 +280,9 @@ public int doSomething() { @NotNull private List getActionTexts() { - return new ConvertFromBDDMockitoGivenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()) - .stream() - .map(action -> (ConvertStubbingAction) action) - .map(ConvertStubbingAction::getTo) - .map(StubbingApproach::getPresentableText) - .collect(toList()); + return map( + new ConvertFromBDDMockitoGivenIntention().actionSelectionOptions(getEditor(), getFile()), + action -> action.getTemplatePresentation().getText() + ); } } diff --git a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntentionTest.java index e34834f6..f788308c 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromBDDMockitoWillIntentionTest.java @@ -2,12 +2,11 @@ package com.picimako.mockitools.intention.convert.stub; -import static java.util.stream.Collectors.toList; +import static com.intellij.util.containers.ContainerUtil.map; import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -367,11 +366,9 @@ public int doSomething() { @NotNull private List getActionTexts() { - return new ConvertFromBDDMockitoWillIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()) - .stream() - .map(action -> (ConvertStubbingAction) action) - .map(ConvertStubbingAction::getTo) - .map(StubbingApproach::getPresentableText) - .collect(toList()); + return map( + new ConvertFromBDDMockitoWillIntention().actionSelectionOptions(getEditor(), getFile()), + action -> action.getTemplatePresentation().getText() + ); } } diff --git a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntentionTest.java index 27cc34d4..74e27dd0 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoDoIntentionTest.java @@ -2,12 +2,11 @@ package com.picimako.mockitools.intention.convert.stub; -import static java.util.stream.Collectors.toList; +import static com.intellij.util.containers.ContainerUtil.map; import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -357,11 +356,9 @@ public int doSomething() { @NotNull private List getActionTexts() { - return new ConvertFromMockitoDoIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()) - .stream() - .map(action -> (ConvertStubbingAction) action) - .map(ConvertStubbingAction::getTo) - .map(StubbingApproach::getPresentableText) - .collect(toList()); + return map( + new ConvertFromMockitoDoIntention().actionSelectionOptions(getEditor(), getFile()), + action -> action.getTemplatePresentation().getText() + ); } } diff --git a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntentionTest.java index 01a774da..48f88714 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertFromMockitoWhenIntentionTest.java @@ -2,12 +2,11 @@ package com.picimako.mockitools.intention.convert.stub; -import static java.util.stream.Collectors.toList; +import static com.intellij.util.containers.ContainerUtil.map; import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -271,11 +270,9 @@ public int doSomething() { @NotNull private List getActionTexts() { - return new ConvertFromMockitoWhenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()) - .stream() - .map(action -> (ConvertStubbingAction) action) - .map(ConvertStubbingAction::getTo) - .map(StubbingApproach::getPresentableText) - .collect(toList()); + return map( + new ConvertFromMockitoWhenIntention().actionSelectionOptions(getEditor(), getFile()), + action -> action.getTemplatePresentation().getText() + ); } } diff --git a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingActionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingActionTest.java index 4c50dfa9..9c41918d 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingActionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/stub/ConvertStubbingActionTest.java @@ -3,7 +3,7 @@ package com.picimako.mockitools.intention.convert.stub; import com.picimako.mockitools.MockitoolsActionTestBase; -import com.picimako.mockitools.StubbingApproach; +import com.picimako.mockitools.dsl.StubbingApproach; import org.junit.jupiter.api.Test; /** diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyActionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyActionTest.java index 6765b6fa..86fcebaf 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyActionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertBDDMockitoThenToInOrderVerifyActionTest.java @@ -7,10 +7,6 @@ /** * Integration test for {@link com.picimako.mockitools.intention.convert.verification.bddmockitothen.ConvertBDDMockitoThenToInOrderVerifyAction}. - *

    - * NOTE: for some unknown reason these tests have become a bit flaky after upgrading to Java 21. The import statement - * of 'org.mockito.BDDMockito' is sometimes removed, sometimes not, from the 'after' text. Thus, they are validated - * with allowing the result to contain or to not contain that import statement. */ class ConvertBDDMockitoThenToInOrderVerifyActionTest extends MockitoolsActionTestBase { @@ -18,7 +14,7 @@ class ConvertBDDMockitoThenToInOrderVerifyActionTest extends MockitoolsActionTes @Test void testConvertsBDDMockitoThenToInOrderVerifyWithNewInOrderWithoutVerificationMode() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), """ import org.mockito.BDDMockito; import org.mockito.Mockito; @@ -34,6 +30,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -52,7 +49,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyWithNewInOrderWithVerificationMode() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), """ import org.mockito.BDDMockito; import org.mockito.Mockito; @@ -68,6 +65,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -86,7 +84,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyReusingSpecifiedInOrderWithoutVerificationMode() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), """ import org.mockito.BDDMockito; import org.mockito.InOrder; @@ -104,6 +102,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -122,7 +121,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyReusingSpecifiedInOrderWithVerificationMode() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(false), """ import org.mockito.BDDMockito; import org.mockito.InOrder; @@ -140,6 +139,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -160,7 +160,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyWithNewInOrderWithoutVerificationModeInSelection() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), """ import org.mockito.BDDMockito; import org.mockito.Mockito; @@ -177,6 +177,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -196,7 +197,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyWithNewInOrderWithVerificationModeInSelection() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), """ import org.mockito.BDDMockito; import org.mockito.Mockito; @@ -213,6 +214,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -232,7 +234,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyReusingSpecifiedInOrderWithoutVerificationModeInSelection() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), """ import org.mockito.BDDMockito; import org.mockito.InOrder; @@ -251,6 +253,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -270,7 +273,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyReusingSpecifiedInOrderWithVerificationModeInSelection() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), """ import org.mockito.BDDMockito; import org.mockito.InOrder; @@ -289,6 +292,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; @@ -308,7 +312,7 @@ public void doSomething() { @Test void testConvertsBDDMockitoThenToInOrderVerifyWithNewInOrderWithVerificationModeInSelectionMultipleMockObjects() { - checkActionFlexible(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), + checkAction(() -> new ConvertBDDMockitoThenToInOrderVerifyAction(true), """ import org.mockito.BDDMockito; import org.mockito.Mockito; @@ -326,6 +330,7 @@ public void doSomething() { } }""", """ + import org.mockito.BDDMockito; import org.mockito.InOrder; import org.mockito.Mockito; diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionInBulkTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionInBulkTest.java index 78820b2c..d9155f9d 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionInBulkTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionInBulkTest.java @@ -6,7 +6,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.junit.jupiter.api.Test; @@ -258,7 +258,7 @@ public void doSomething() { } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); assertThat(actions).containsExactly( @@ -288,7 +288,7 @@ public void doSomething() { } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -318,7 +318,7 @@ public void doSomething() { } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -347,7 +347,7 @@ public void doSomething() { } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionTest.java index 5245e469..8e0e4cde 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/bddmockitothen/ConvertFromBDDMockitoThenIntentionTest.java @@ -6,7 +6,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import com.picimako.mockitools.intention.convert.verification.NoActionAvailableAction; import org.junit.jupiter.api.Test; @@ -181,7 +181,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -203,7 +203,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -225,7 +225,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -246,7 +246,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -270,7 +270,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -293,7 +293,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -318,7 +318,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -342,7 +342,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromBDDMockitoThenIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionInBulkTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionInBulkTest.java index eb3a0996..1b6e2741 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionInBulkTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionInBulkTest.java @@ -6,7 +6,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.junit.jupiter.api.Test; @@ -263,7 +263,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -289,7 +289,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -311,7 +311,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -339,7 +339,7 @@ void testMethod(){ } } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -361,7 +361,7 @@ void testMethod(){ } } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionTest.java index 4f1acb13..70e5e4ac 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/inorderverify/ConvertFromInOrderVerifyIntentionTest.java @@ -6,7 +6,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.junit.jupiter.api.Test; @@ -166,7 +166,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -192,7 +192,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -214,7 +214,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -242,7 +242,7 @@ void testMethod(){ } } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -264,7 +264,7 @@ void testMethod(){ } } }"""); List> actions = map( - new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromInOrderVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntentionTest.java index dccb10f5..cd8da3ef 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/mockedstaticverify/ConvertFromMockedStaticVerifyIntentionTest.java @@ -110,7 +110,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromMockedStaticVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromMockedStaticVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionInBulkTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionInBulkTest.java index b607972d..be82f647 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionInBulkTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionInBulkTest.java @@ -3,7 +3,7 @@ package com.picimako.mockitools.intention.convert.verification.mockitoverify; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionTest.java b/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionTest.java index 1129b849..857183f9 100644 --- a/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionTest.java +++ b/src/test/java/com/picimako/mockitools/intention/convert/verification/mockitoverify/ConvertFromMockitoVerifyIntentionTest.java @@ -6,7 +6,7 @@ import static org.assertj.core.api.Assertions.assertThat; import com.intellij.codeInsight.intention.IntentionAction; -import com.picimako.mockitools.Convention; +import com.picimako.mockitools.dsl.Convention; import com.picimako.mockitools.intention.convert.EnforceConventionAwareIntentionTestBase; import org.junit.jupiter.api.Test; @@ -152,7 +152,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromMockitoVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromMockitoVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -172,7 +172,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromMockitoVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromMockitoVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); @@ -194,7 +194,7 @@ void testMethod(){ } }"""); List> actions = map( - new ConvertFromMockitoVerifyIntention().actionSelectionOptions(getFixture().getEditor(), getFixture().getFile()), + new ConvertFromMockitoVerifyIntention().actionSelectionOptions(getEditor(), getFile()), Object::getClass ); diff --git a/src/test/java/com/picimako/mockitools/util/PsiMethodUtilTest.java b/src/test/java/com/picimako/mockitools/util/PsiMethodUtilTest.java index 67b5a2c9..5d54fad1 100644 --- a/src/test/java/com/picimako/mockitools/util/PsiMethodUtilTest.java +++ b/src/test/java/com/picimako/mockitools/util/PsiMethodUtilTest.java @@ -216,7 +216,7 @@ public void testMethod() { ApplicationManager.getApplication().invokeAndWait(() -> WriteAction.run(() -> CommandProcessor.getInstance().executeCommand( - getFixture().getProject(), + getProject(), () -> PsiMethodUtil.deleteArguments(methodCall), "Delete", "group.id"))); assertThat(computeBlocking(() -> methodCall.getArgumentList().isEmpty())).isTrue(); @@ -336,6 +336,6 @@ public void testMethod() { //Helpers private PsiMethodCallExpression getMethodCall() { - return computeBlocking(() -> (PsiMethodCallExpression) getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent().getParent()); + return computeBlocking(() -> (PsiMethodCallExpression) getFile().findElementAt(getCaretOffset()).getParent().getParent()); } } diff --git a/src/test/testData/inspection/NoMethodCallArgumentMockitoV3Test.java b/src/test/testData/inspection/NoMethodCallArgumentMockitoV3Test.java new file mode 100644 index 00000000..d7958fb5 --- /dev/null +++ b/src/test/testData/inspection/NoMethodCallArgumentMockitoV3Test.java @@ -0,0 +1,24 @@ +import java.util.List; + +import org.mockito.Mockito; + +public class NoMethodCallArgumentMockitoV3Test { + + public void testMethod() { + //Highlight cases + Mockito.mock(Object.class, Mockito.withSettings().extraInterfaces()); + Mockito.verifyNoInteractions(); + Mockito.verifyNoMoreInteractions(); + Mockito.verifyZeroInteractions(); + Mockito.inOrder(); + Mockito.ignoreStubs(); + + //No-highlight cases + Object mock = Mockito.mock(Object.class, Mockito.withSettings().extraInterfaces(List.class)); + Mockito.verifyNoInteractions(mock); + Mockito.verifyNoMoreInteractions(mock); + Mockito.verifyZeroInteractions(mock); + Mockito.inOrder(mock); + Mockito.ignoreStubs(mock); + } +} diff --git a/src/test/testData/inspection/NoMethodCallArgumentTest.java b/src/test/testData/inspection/NoMethodCallArgumentTest.java index c62e7a68..bb5fbe39 100644 --- a/src/test/testData/inspection/NoMethodCallArgumentTest.java +++ b/src/test/testData/inspection/NoMethodCallArgumentTest.java @@ -9,7 +9,6 @@ public void testMethod() { Mockito.mock(Object.class, Mockito.withSettings().extraInterfaces()); Mockito.verifyNoInteractions(); Mockito.verifyNoMoreInteractions(); - Mockito.verifyZeroInteractions(); Mockito.inOrder(); Mockito.ignoreStubs(); @@ -17,7 +16,6 @@ public void testMethod() { Object mock = Mockito.mock(Object.class, Mockito.withSettings().extraInterfaces(List.class)); Mockito.verifyNoInteractions(mock); Mockito.verifyNoMoreInteractions(mock); - Mockito.verifyZeroInteractions(mock); Mockito.inOrder(mock); Mockito.ignoreStubs(mock); }