Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullPointerException in java.lang.reflect.Method.getParameterTypes #2026

Closed
andrei-ivanov opened this issue Aug 27, 2020 · 34 comments
Closed

Comments

@andrei-ivanov
Copy link

Trying to upgrade Mockito from 3.4.6 (3.4.8 wasn't published to Maven central) to anything 3.5.x (3.5.7 included) and I get some weird NPEs, which don't happen if I run each test on its own:

[INFO] Running core.rest.exception.mapper.NotFoundExceptionMapperTest
07:22:56.767 [main] WARN  core.rest.exception.mapper.NotFoundExceptionMapper - Endpoint not found for path: http://host:8080/404
[ERROR] Tests run: 3, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.019 s <<< FAILURE! - in core.rest.exception.mapper.NotFoundExceptionMapperTest
[ERROR] testExceptionCreatesServerErrorResponseWhenEndpointWasNotMatchedAndIsServiceRequest  Time elapsed: 0.012 s  <<< ERROR!
java.lang.NullPointerException
        at java.base/java.lang.reflect.Method.getParameterTypes(Method.java:311)
        at org.mockito.internal.creation.DelegatingMethod.<init>(DelegatingMethod.java:20)
        at org.mockito.internal.invocation.DefaultInvocationFactory.createMockitoMethod(DefaultInvocationFactory.java:80)
        at org.mockito.internal.invocation.DefaultInvocationFactory.createInvocation(DefaultInvocationFactory.java:59)
        at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:58)
        at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:47)
        at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:129)
        at org.mockito.codegen.HttpHeaders$MockitoMock$2122467463.getHeaderString(Unknown Source)
        at core.rest.exception.mapper.NotFoundExceptionMapper.isServiceRequest(NotFoundExceptionMapper.java:56)
        at core.rest.exception.mapper.NotFoundExceptionMapper.getResponseBuilder(NotFoundExceptionMapper.java:48)
        at core.rest.exception.mapper.NotFoundExceptionMapper.getResponseBuilder(NotFoundExceptionMapper.java:13)
        at core.rest.exception.mapper.BaseExceptionMapper.getResponse(BaseExceptionMapper.java:33)
        at core.rest.exception.mapper.BaseExceptionMapper.toResponse(BaseExceptionMapper.java:28)
        at core.rest.exception.mapper.NotFoundExceptionMapperTest.testExceptionCreatesServerErrorResponseWhenEndpointWasNotMatchedAndIsServiceRequest(NotFoundExceptionMapperTest.java:43)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Initially I thought it might be caused by the new MockedStatic usage, but I've marked those classes with @Disable and the exceptions happen anyway and the test classes that are affected weren't using MockedStatic anyway, so I'm not exactly sure how to investigate this further.

@TimvdLippe
Copy link
Contributor

We are going to need a small reproduction case for us to debug this problem.

@andrei-ivanov
Copy link
Author

Not sure how, since each test ran separatelly is fine and I can't tell which one, when all the suite is ran, creates some sort of corruption, since the @Origin Method invokedMethod is injected as some broken object.

image

@TimvdLippe
Copy link
Contributor

Please create a small reproduction case, e.g. a GitHub project that we can run and debug.

@andrei-ivanov
Copy link
Author

I'll try to do one next week, after I return from vacation, but, in the meantime, I've commented out tests one-by-one, until I discovered what triggers the problem....

One of them has a Mockito.mock(Method.class).
The test itself, ran by itself, works... but if the whole suite is ran, some tests that follow it get the problem described above.

Removing the mocking of the java.lang.reflect.Method made everything green again.

@andrei-ivanov
Copy link
Author

andrei-ivanov commented Sep 4, 2020

I've managed to make a reproducer 🙂
test.zip

@andrei-ivanov
Copy link
Author

Is it ok like this?

@rimuln
Copy link

rimuln commented Dec 5, 2020

I could confirm that have this issue too.

We have in one class

@Mock
private java.lang.reflect.Method method

When this class that contains this mocking is run alone that the test is green in Eclipse and maven too.

When it run with other test all following test failed on this NPE. For me it looks like that after the test with mocked Final class isn't correctly released and next tests instead of call of original java.lang.reflect.Method type calls it on mock.

@junkdog
Copy link

junkdog commented Dec 8, 2020

A temporary workaround is to fork each test, though build times suffer somewhat.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
    <configuration>
        <forkCount>1</forkCount>
        <reuseForks>false</reuseForks>
    </configuration>
</plugin>

@nguyenquoc
Copy link

Also ran into this issue when upgrading from Mockito 3.3.3 to Mockito 3.6.0 (from spring-boot 2.3.6 to 2.4.0). It seems that creating a mock of a particular class/interface in a lot of tests from independent test classes exposes this problem. In my case I am mocking an interface, final is not involved.

@andrei-ivanov 's workaround works, but is quite slow.

@TimvdLippe
Copy link
Contributor

Mocking of classes in java.lang.* is unsupported, as Mockito itself is based on these classes. Therefore, we can't guarantee the correct behavior of Mockito if Mockito is instructed to modify the classes it is built on. This is especially true for any class in java.lang.reflect.*, as Mockito has to make extensive use of reflection to function correctly.

Therefore, I would be inclined to close this issue as WontFix, unless @nguyenquoc you are mocking a non-java.lang.* interface that creates problems?

@andrei-ivanov
Copy link
Author

Any chance to get an error or a warning for these classes if we try to mock them?

@TimvdLippe
Copy link
Contributor

Yes I have worked on a prototype to make that happen: #1833 Sadly I haven't had the time to get back to that.

@rimuln
Copy link

rimuln commented Dec 9, 2020

As we solved it by rewrite unfinished feature. We don't need mock this class now.

Maybe would be nice solve it in same way as is e.g. for String class so some error message that point to this. From current documentation it isn't really clear if this is or isn't supported. There is mentioned that some of aren't supported.

@TimvdLippe
Copy link
Contributor

This is documented in our wiki: https://github.com/mockito/mockito/wiki/How-to-write-good-tests#dont-mock-a-type-you-dont-own Our intention with DoNotMock is that we make this even more explicit.

@rimuln
Copy link

rimuln commented Dec 9, 2020

I read the #1833 and sounds as good improvement.

Problem with wiki is that you read it probably when you start with Mockito as newer. We are using in our project mockito long time. We started with 1.10.19 version. So we mostly read now only what is new = e.g. Mockito class javadoc where is enumeration of features. So I mean the point 39 where is about mocking final (new feature for us that allow leave Powermock in combination of static mock). And here is not fully clear. Especially when for String you got error but for java.lang.reflect.* no. I wasn't author of our test. Just find what was problem. Original implementation is changed in the meantime and we don't mock java.lang.reflect.* now.

@nguyenquoc
Copy link

@TimvdLippe : In my case I am mocking the spring-beans org.springframework.beans.factory.BeanFactory interface:

BeanFactory beanFactory = mock(BeanFactory.class);

If I run the test class by itself, then the mock is successful. If I run the test class in a suite of tests (e.g. mvn install on a whole product), then the mock is created, but is defective, e.g. toString() results in a null pointer exception, and using the mock also results in a null pointer exception.

My tests work with Mockito 3.3.3, but fail with 3.6.0.

@McKratt
Copy link

McKratt commented Jan 12, 2021

@TimvdLippe In my case I'm mocking also my own classes and get the same NPE. What can we do to help you to solve this issue ?

I don't know if it helps but in Java 15 I got this log :

[ERROR] getCurrentCallContext_should_return  Time elapsed: 0.001 s  <<< ERROR!
java.lang.NullPointerException: Cannot invoke "[Ljava.lang.Class;.clone()" because "this.parameterTypes" is null
	at ch.mobi.jap.context.callcontext.internal.DefaultCallContextManagerTest.getCurrentCallContext_should_return(DefaultCallContextManagerTest.java:91)

@TimvdLippe
Copy link
Contributor

Sadly I don't have a lot of free cycles to investigate this issue. What would help is to perform a bisect of Mockito versions to figure out which specific PR is causing issues. You can use our Bintray repository which hosts all of our versions to hopefully determine the exact version at which things start to break: https://bintray.com/mockito/maven/mockito

@McKratt
Copy link

McKratt commented Jan 12, 2021

Sadly I don't have a lot of free cycles to investigate this issue. What would help is to perform a bisect of Mockito versions to figure out which specific PR is causing issues. You can use our Bintray repository which hosts all of our versions to hopefully determine the exact version at which things start to break: https://bintray.com/mockito/maven/mockito

I tried different version. And it is the 3.5.0 version that made the break for me. When I pass from mockito(-jupiter) 3.4.6 to 3.5.0 everything goes wrong.

There are not so much diff : v3.4.6...v3.5.0, but for me simple (humble, grateful ;-)) user, it is Klingon !

@TimvdLippe
Copy link
Contributor

The most likely candidate for the regression is probably #2004 @raphw does this exception ring any bells?

@raphw
Copy link
Member

raphw commented Jan 12, 2021

Mocking Method is certainly an issue. Mocking classes that Mockito (or Byte Buddy) use internally will change the behavior of Mockito (or Byte Buddy) and lead to errors.

As for the error occuring when not mocking core classes. Did you try to remove mocks until the issue no longer occurs? This can help to break it down to see which class would cause this. I'd need more detail to to look into it.

@McKratt
Copy link

McKratt commented Jan 13, 2021

Mocking Method is certainly an issue. Mocking classes that Mockito (or Byte Buddy) use internally will change the behavior of Mockito (or Byte Buddy) and lead to errors.

As for the error occuring when not mocking core classes. Did you try to remove mocks until the issue no longer occurs? This can help to break it down to see which class would cause this. I'd need more detail to to look into it.

I finally reached the issue. I was mocking Method too, but not in any test in error, not so easy to find, I have more than 300 in this project. That's means I also used the feature to mock final object mock-maker-inline. It was working before but I understand that it is not a good practise. Thank you for your help to point the real error.

@kenvhnguyen35
Copy link

kenvhnguyen35 commented Jun 2, 2021

Hi everyone, I have exactly this issue for existing tests migrating to any mockito from 3.5.0 to the current latest 3.10.0
@andrei-ivanov How did you go about this in the end?

@andrei-ivanov
Copy link
Author

I removed the mocking of Method, as suggested 😀

@kenvhnguyen35
Copy link

I removed the mocking of Method, as suggested 😀

Yes, indeed, now I found one mock of the Method which indeed the culprit. So for everyone else landing on this page in the future, try your best to find in your code mocking of java.lang.reflect.Method and address those tests.

@ostecke
Copy link

ostecke commented Jun 10, 2021

This took me three days of searching... Thanks for the help and suggestions!

@TimvdLippe
Copy link
Contributor

Unfortunately, Method is one of the classes that Mockito relies on internally for its behavior. Stubbing Method will therefore lead to undefined behavior. Additionally, it is advised not to mock classes you don't own: https://github.com/mockito/mockito/wiki/How-to-write-good-tests#dont-mock-a-type-you-dont-own We are working on improving the user experience by working on a DoNotMock feature to avoid mocking classes/methods that are known to crash Mockito internals (#1833). Therefore, I am closing this as "Infeasible". Apologies for the uninformative exception that is thrown.

MEspositoE14s added a commit to CMSgov/dpc-app that referenced this issue Jul 18, 2023
## 🎫 Ticket

https://jira.cms.gov/browse/DPC-3494

## 🛠 Changes

Updated Mockito from 3.4.6 to 5.4.0.
Removed mocking of Method class from FHIRRequestFeatureTest.
Updated BaseResourceUnitTest to use new MockedStatic and
MockedConstruction.

## ℹ️ Context for reviewers

We were running an old version of Mockito that didn't support mocking
static or final methods, or constructors, and this was preventing us
from pushing code coverage as far as we otherwise could. The new version
of Mockito was also causing unit tests to fail randomly and erratically.

The new version was causing tests to fail because an old test in
`FHIRRequestFeatureTest` was mocking the `Method` class from
`java.lang.reflect`. Mockito depends on this class, along with a few
others from that package, and mocking them is essentially rewriting
Mockito's plumbing while it's running. This leads to any tests running
afterwards potentially failing in random ways. See issue
[here](mockito/mockito#2026 (comment))
for a better explanation.

## ✅ Acceptance Validation

We're running the new version of Mockito, we're using some of its new
features, and all unit tests are passing.

## 🔒 Security Implications

- [ ] This PR adds a new software dependency or dependencies.
- [ ] This PR modifies or invalidates one or more of our security
controls.
- [ ] This PR stores or transmits data that was not stored or
transmitted before.
- [ ] This PR requires additional review of its security implications
for other reasons.

If any security implications apply, add Jason Ashbaugh (GitHub username:
StewGoin) as a reviewer and do not merge this PR without his approval.
@trajano
Copy link

trajano commented Aug 10, 2023

Since mocking method causes an issue one approach to avoid it is to use reflection to get the real method instead. e.g.

final var method = IdentifiableEntity.class.getDeclaredMethod("getId");

@Avinm
Copy link

Avinm commented Sep 27, 2023

@TimvdLippe: While I understand concerns with mocking java.lang.reflect.Method, it used to work without issues in prior versions of Mockito and the upgrade to the latest version created non-deterministic test failures which were non-trivial to debug in our test suite with ~1000 tests that we were running in parallel.

Would you be open to a PR that adds this as an enforcement to the DefaultDoNotMockEnforcer? For now I was able to narrow this down by writing my own (our issue turned out to be a hidden mocking of Method via Answers.RETURNS_DEEP_STUBS)

public class DoNotMockJavaLangMethodEnforcer extends DefaultDoNotMockEnforcer {

    @Override
    public String checkTypeForDoNotMockViolation(Class<?> type) {
        if (type.equals(Method.class)) { // TODO: Possibly other classes
            return "Cannot mock java.lang.reflect.Method. Ref: https://github.com/mockito/mockito/issues/2026";
        } else  {
            return super.checkTypeForDoNotMockViolation(type);
        }
    }
}

For others that may benefit, the above file has to be registered as a plugin by creating:
mockito-extensions/org.mockito.plugins.DoNotMockEnforcer with contents like so:

com.your.package.DoNotMockJavaLangMethodEnforcer

@TimvdLippe
Copy link
Contributor

We should indeed add this to DefaultDoNotMockEnforcer, but we can only do that in the next major version. That way, it indeed becomes more apparent that you should not mock these, to avoid the complicated debugging that you experienced. Apologies for that.

Can you send us a PR that updates DefaultDoNotMockEnforcer with this type, but doesn't throw on it just yet? Then we can collect all problematic types and turn enforcement on in Mockito 6.

@Avinm
Copy link

Avinm commented Sep 29, 2023

Since I am only aware of java.lang.Method, I don't see the value in just capturing the one type and not doing anything about it for now. Unless you are aware of more types and want to do maybe a log.warn or something?

@trajano
Copy link

trajano commented Sep 29, 2023

I think Field as well.

@TimvdLippe
Copy link
Contributor

Any type in java.lang.reflect would be incompatible

@bertysentry
Copy link

I've encountered the very same problem, and it was caused by a conflict where the same class was mocked in different tests. When these tests ran simultaneously (in the same JVM), Mockito would randomly throw a NPE.

Using @junkdog's solution fixed the problem (force Maven's surefire plugin to not use the same JVM for the different tests).

I just want to highlight that this problem is not caused only by mocking java.lang.*.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests