Skip to content

Add PowerMockWhiteboxToJavaReflection recipe#944

Merged
timtebeek merged 6 commits intomainfrom
bmuschko/whitebox-migration
Mar 27, 2026
Merged

Add PowerMockWhiteboxToJavaReflection recipe#944
timtebeek merged 6 commits intomainfrom
bmuschko/whitebox-migration

Conversation

@bmuschko
Copy link
Copy Markdown
Contributor

@bmuschko bmuschko commented Mar 26, 2026

Summary

  • Add PowerMockWhiteboxToJavaReflection recipe that replaces org.powermock.reflect.Whitebox calls (setInternalState, getInternalState, invokeMethod) with plain java.lang.reflect equivalents
  • Wire recipe into the ReplacePowerMockito YAML chain (before CleanupPowerMockImports)
  • Add parserClasspath("org.powermock:powermock-reflect:1.6.5") to build.gradle.kts so MethodMatcher can resolve Whitebox types
  • Add powermock-reflect-1 to ReplacePowerMockitoIntegrationTest classpath so the integration test parser can resolve Whitebox method calls now that the recipe chain includes this new recipe

Why typeValidationOptions(methodInvocations(false)) is needed

The replacement templates generate java.lang.reflect.Field / java.lang.reflect.Method calls via string-based JavaTemplate.builder(...). During template application, the parser doesn't have the full JDK reflection types on its classpath, so the generated method invocations (field.setAccessible(true), field.set(...), method.invoke(...)) lack fully resolved method types. Disabling method invocation validation suppresses errors for these unresolved types. This is consistent with the existing pattern in ReplacePowerMockitoIntegrationTest.

@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Mar 26, 2026
@bmuschko bmuschko added the enhancement New feature or request label Mar 26, 2026
Replace `org.powermock.reflect.Whitebox` calls with plain Java
reflection as part of the ReplacePowerMockito recipe chain.

Handles setInternalState, getInternalState, and invokeMethod by
expanding each call into getDeclaredField/getDeclaredMethod +
setAccessible + get/set/invoke. Uses VariableNameUtils to avoid
name collisions when multiple Whitebox calls target the same field.
@bmuschko bmuschko force-pushed the bmuschko/whitebox-migration branch from 07a6548 to c77ea54 Compare March 26, 2026 17:00
bmuschko and others added 3 commits March 26, 2026 11:15
The classpath.tsv.gz type table is what CI uses for type resolution
in tests. The previously committed JAR is unnecessary when the type
table includes the artifact.
- Inline the named WhiteboxVisitor into an anonymous class
- Use immediate return for maybeAutoFormat
- Remove unnecessary powermock-reflect-1 classpath from integration test
class MyServiceTest {
void testInvokeWithArgs() throws Exception {
MyService service = new MyService();
Method greetMethod = service.getClass().getDeclaredMethod("greet", "World".getClass());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the .getClass() looks a bit odd; would it help to use type information where available, and only fall back where needed?

Also: would this correctly handle sub classes? E.g. if the argument is defined as an interface on the method declaration, but the argument passed in is a concrete instance, do we then still retrieve the correct method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the .getClass() looks a bit odd; would it help to use type information where available, and only fall back where needed?

It looks like you already relaxed the type information via #{any(java.lang.Object)}. Thanks for that!

Also: would this correctly handle sub classes?

Good point. This will likely be an issue. I can create a follow up PR though I believe covering this use case can be handled later when we actually run into it.

Untyped #{any()} placeholders caused the template parser to infer the
argument's actual type (e.g. MyService), which is not on the template
parser's classpath. This broke type resolution for the entire method
chain (.getClass(), .getDeclaredField(), .setAccessible(), etc.).

Using #{any(java.lang.Object)} ensures the substitution always resolves
against a JDK type, fixing type attribution. Also inlines the JAVA_PARSER
field and removes the now-unnecessary methodInvocations(false) from the
unit test.
@github-project-automation github-project-automation Bot moved this from In Progress to Ready to Review in OpenRewrite Mar 27, 2026
@timtebeek timtebeek merged commit e679ace into main Mar 27, 2026
1 check passed
@timtebeek timtebeek deleted the bmuschko/whitebox-migration branch March 27, 2026 13:38
@github-project-automation github-project-automation Bot moved this from Ready to Review to Done in OpenRewrite Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request mockito

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants