openrewrite / openrewrite/rewrite-testing-frameworks
Issue when MockedStatic field is defined with an inline Mockito.mockStatic initialiser
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 100
- Forks
- 105
- Avg merge
- 2h 25m
- Merged PRs (30d)
- 9
Description
What version of OpenRewrite are you using?
I am using
- Maven plugin:
org.openrewrite.maven:rewrite-maven-pluginv6.44.0 org.openrewrite.recipe:rewrite-springv6.35.0org.openrewrite.recipe:rewrite-testing-frameworksv3.42.0 (pulled in transitively by rewrite-spring)
The bug lives in org.openrewrite.java.testing.mockito.CloseUnclosedStaticMocks, in rewrite-testing-frameworks. It is reached transitively via:
org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 → spring-boot-31.yml's org.openrewrite.java.testing.mockito.Mockito4to5Only → org.openrewrite.java.testing.mockito.Mockito1to4Migration → org.openrewrite.java.testing.mockito.CloseUnclosedStaticMocks (last recipe in Mockito1to4Migration's recipeList, per mockito.yml).
How are you running OpenRewrite?
I am using the Maven plugin (rewrite-maven-plugin:run), on a single-module project, running a declarative recipe chain that includes org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5.
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.44.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5</recipe>
</activeRecipes>
</configuration>
</plugin>
What is the smallest, simplest way to reproduce the problem?
package com.example;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
class MyTest {
private static final MockedStatic<CryptoUtil> MOCKED_CRYPTO_UTILS = Mockito.mockStatic(CryptoUtil.class);
}
Any test class that declares a MockedStatic field with an inline Mockito.mockStatic(...) initializer directly in the class body (as opposed to assigning it in a @BeforeAll/@BeforeEach lifecycle method) triggers the crash:
Root cause (confirmed by reading CloseUnclosedStaticMocks's source from rewrite-testing-frameworks-3.42.0-sources.jar): its visitBlock() override fires on any J.Block, but a class body is also represented as a J.Block in the LST, just like a method body. The recipe guards against lifecycle-method blocks (insideLifecycleMethod()), but not against the class-body block itself. So it mistakes this field declaration for an "unclosed local variable" inside a block, and attempts to wrap it in a try (#{any()}) {} template — which isn't valid for a field declaration (its modifiers, e.g. private static final, aren't compatible with a try-with-resources resource declaration). The resulting JavaTemplate.apply() call generates 2 statements instead of the expected 1 replacement statement, and throws.
Note that the recipe's separate assignment-based path (visitAssignment / DeclareMockVarAndClose, used when mockStatic() appears as VAR = Mockito.mockStatic(X.class); rather than as an inline field initializer) works correctly, including correctly recognizing a pre-existing manual .close() call as already handling cleanup. The bug is specific to the inline-field-initializer form.
What did you expect to see?
Since the field's MockedStatic is already closed manually elsewhere (e.g. in a matching @AfterAll), I'd expect the recipe to make no change, e.g.:
package com.example;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
class MyTest {
private static final MockedStatic<CryptoUtil> MOCKED_CRYPTO_UTILS = Mockito.mockStatic(CryptoUtil.class);
@AfterAll
static void afterAll() {
MOCKED_CRYPTO_UTILS.close();
}
}
What did you see instead?
At minimum, I'd expect the recipe to not crash the whole run — e.g. it could correctly detect the existing .close() call the way it already does for the assignment-based form, or leave inline-initialized class-body fields alone entirely if it can't safely rewrite them.
What is the full stack trace of any errors you encountered?
[ERROR] Failed to execute goal org.openrewrite.maven:rewrite-maven-plugin:6.44.0:run (default-cli) on project AppFunctionalTests: Execution default-cli of goal org.openrewrite.maven:rewrite-maven-plugin:6.44.0:run failed: Error while visiting app_name/AppFunctionalTests/filename.java: java.lang.IllegalArgumentException: Expected a template that would generate exactly one statement to replace one statement, but generated 2. Template:
[ERROR] try(__P__.<org.mockito.MockedStatic<Unknown>>/*__p0__*/p()) {}
[ERROR] Substitutions:
[ERROR] Substitutions(code=try(#{any()}) {}, genericTypes=[], parameters=[private static final MockedStatic<CryptoUtil> MOCKED_CRYPTO_UTILS = Mockito.mockStatic(CryptoUtil.class)], propertyPlaceholderHelper=org.openrewrite.internal.PropertyPlaceholderHelper@72eca5c5, typeVariables=[])
[ERROR] Statement:
[ERROR] private static final MockedStatic<CryptoUtil> MOCKED_CRYPTO_UTILS = Mockito.mockStatic(CryptoUtil.class)
at org.openrewrite.java.JavaTemplate.apply(JavaTemplate.java:...)
at org.openrewrite.java.testing.mockito.CloseUnclosedStaticMocks$CloseUnclosedStaticMocksVisitor.toTryWithResource(CloseUnclosedStaticMocks.java:...)
at org.openrewrite.java.testing.mockito.CloseUnclosedStaticMocks$CloseUnclosedStaticMocksVisitor.visitBlock(CloseUnclosedStaticMocks.java:...)
...
Are you interested in contributing a fix to OpenRewrite?
No.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in org.openrewrite.java.testing.mockito.CloseUnclosedStaticMocks, especially the visitBlock() path and its lifecycle-method guard, using the inline MockedStatic field reproducer in the issue. Add regression coverage for this class-body case and verify the recipe no longer throws while preserving the field when cleanup is already handled, then run the relevant rewrite-testing-frameworks tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100