Regression: MethodNotFoundException #717

Closed
andrei-ivanov opened this Issue Nov 9, 2016 · 14 comments

Projects

None yet

6 participants

@andrei-ivanov
andrei-ivanov commented Nov 9, 2016 edited

Trying to upgrade from 1.6.4 to 1.6.6, I ran into this error:

org.powermock.reflect.exceptions.MethodNotFoundException: No methods matching the name(s) accept were found in the class hierarchy of class java.lang.Object.
	at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1720)
	at org.powermock.reflect.internal.WhiteboxImpl.getMethods(WhiteboxImpl.java:1745)
	at org.powermock.reflect.internal.WhiteboxImpl.getBestMethodCandidate(WhiteboxImpl.java:983)
	at org.powermock.core.MockGateway$MockInvocation.findMethodToInvoke(MockGateway.java:317)
	at org.powermock.core.MockGateway$MockInvocation.init(MockGateway.java:356)
	at org.powermock.core.MockGateway$MockInvocation.<init>(MockGateway.java:307)
	at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:142)
	at org.powermock.core.MockGateway.methodCall(MockGateway.java:125)
	at InstanceFacadeImplTest.pendingInstanceStatusProcessorShouldDoNothing(InstanceFacadeImplTest.java:91)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:316)
	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:300)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:288)
	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:208)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:147)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:121)
	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:123)
	at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
	at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
	at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
public enum InstanceStatus {
	PENDING;
}
import java.util.function.Consumer;

public class InstanceFacadeImpl implements InstanceFacade {
	final Map<InstanceStatus, Consumer<Instance>> instanceStatusProcessors = new HashMap<>();

	{
		instanceStatusProcessors.put(InstanceStatus.PENDING, instance -> {
			// NOP
		});
	}
}
@RunWith(PowerMockRunner.class)
@PowerMockListener(AnnotationEnabler.class)
@PowerMockIgnore({ "javax.management.*" })
public class InstanceFacadeImplTest {

	private InstanceFacadeImpl instanceFacade;

	@Before
	public void setup() throws Exception {
		instanceFacade = new InstanceFacadeImpl();
	}

	@Test
	public void pendingInstanceStatusProcessorShouldDoNothing() throws Exception {
		replayAll();
		instanceFacade.instanceStatusProcessors.get(InstanceStatus.PENDING).accept(null);
		verifyAll();
	}
}
@andrei-ivanov
andrei-ivanov commented Nov 9, 2016 edited

As far as I see, it reaches org.powermock.reflect.internal.proxy.ProxyFrameworks.isCglibProxyClass(Class<?> clazz), which mistakes the lambda as a cglib proxy...

instance = InstanceFacadeImpl$$Lambda$1/1881218633@87b5b49
methodName = accept

This is from org.powermock.reflect.internal.proxy.ProxyFrameworks.getUnproxiedType(Class<?> type)
After that, the type becomes java.lang.Object, where it doesn't find the method.

@thekingnothing
Member

Thank you for investigation! You're welcome to provide a pull request with fix :)

@thekingnothing thekingnothing added this to the PowerMock 1.6.7 milestone Nov 9, 2016
@andrei-ivanov

Hmm, I'm not sure how the fix should look.
As far as I see on StackOverflow, there's no proper way to identify a lambda.
Looking for $$Lambda is not guaranteed to be portable or to be the same in the future versions.

I did however found an answer on how to properly identify cglib proxies.
It's actually done that way in another place in PowerMock: org.powermock.api.easymock.PowerMock.isEasyMocked(Object mock):

import net.sf.cglib.proxy.Enhancer;

private static boolean isEasyMocked(Object mock) {
    return Enhancer.isEnhanced(mock.getClass()) || Proxy.isProxyClass(mock.getClass());
}

This would mean that powermock-reflect would have a compile dependency on cglib-nodep, not just test, like it is now.

@thekingnothing
Member

Unfortunately, we cannot introduce a new dependencies to powermock-reflect, because all other modules depends on it, and soon mockito module will not depend on cglib (Mockito 2 uses ByteBuddy instead CgLib).

I snooped the approach with CGLIB_CLASS_SEPARATOR from Spring.

Now, I think the right way will be to implement the same approach as in Enhancer.isEnhanced

    public boolean isCglibProxyClass(Class<?> clazz) {
        Method[] methods = class.getDeclaredMethod();
        for(Method m: method){
            if( "CGLIB$SET_THREAD_CALLBACKS".equals(m.getName() && m.getParameterTypes().lenght = 1)
                 return true;
        }
        return false;
    }

@andrei-ivanov

Indeed, as I've already tried the Enhancer.isEnhanced approach and it fails for Mockito CgLib generated classes, as they've modified the parameters of CGLIB$SET_THREAD_CALLBACKS.

@andrei-ivanov

I wonder how should I handle the unit test, as the project level is set to 1.6, which doesn't allow lambdas.

@thekingnothing
Member

You can handle the test as it did in mockito case. Just create a new module junit4-java8-test and set language level to 1.8.

Thank you a lot for your help!

@andrei-ivanov

Hmm, junit4-java8-test and not reflection-java8-test?

@thekingnothing
Member

It's a really good question. I meant create a module a put in it a test for your case. Case: using EasyMock + PowerMock with class with lambda (and maybe create a same case for Mockito).

But, you're right. ProxyFrameworks also has to be tested, that it correctly works with lambda classes. However, we can extends case and say that the ProxyFrameworks has to work correctly with any runtime generated class. It should returns true if argument is a proxy class of known frameworks and false otherwise.

@eBraund
eBraund commented Nov 23, 2016 edited

I have this exception too, in 2 places in my code. The tests did work previously with mockito 1.6.5 but started to fail when I switched to 1.6.6.

The first occurrence was when mocking a Map. I got around the problem by using a real map and just filling it with mock objects.

The second occurrence was when calling next() on a mock of java.sql.ResultSet. I've spent all afternoon trying to solve it but have now given up and switched back to 1.6.5 (which is a shame as going to 1.6.6 fixed some issues I was experiencing using EclEmma with Eclipse to get code coverage).

I wonder if it is anything to do with Map and ResultSet both being interfaces?

NB - I'm using mockito 1.10.19 and junit 4.10

@Aaron1011
Aaron1011 commented Dec 19, 2016 edited

@andrei-ivanov Are you still working on this? If not, I'd be willing to work on a fix.

@thekingnothing thekingnothing added a commit that referenced this issue Jan 26, 2017
@thekingnothing thekingnothing Fix #717: Regression: MethodNotFoundException
Signed-off-by: Arthur Zagretdinov <arthur.zagretdinov@outlook.com>
a300861
@thekingnothing thekingnothing added a commit that referenced this issue Jan 26, 2017
@thekingnothing thekingnothing Fix #717: Regression: MethodNotFoundException (#741)
Signed-off-by: Arthur Zagretdinov <arthur.zagretdinov@outlook.com>
ddda468
@rushikeshr

I have encountered the similar error.
I am unable to execute my junit testcases with PowerMockito 1.6.6 jars. I Get below error on executing any testcase after using PowerMockito 1.6.6 jars But when I use powerockito 1.6.2 my tastecase executes successfully.
org.powermock.reflect.exceptions.MethodNotFoundException: No methods matching the name(s) createStatement were found in the class hierarchy of class java.lang.Object. at

BUT I NEED TO USE POWERMOCKITO 1.6.6 AND ABOVE VERSION FOR COMPATIBLITY WITH OTHER TOOLS IN THE PROJECT. Is this issue fixed in POWERMOCKITO 1.6.7 ?

@thekingnothing
Member

@rushikeshr the issue is fixed in PowerMock 1.7.0 (Release notes). In Maven Central available PowerMock 1.7.0RC2 with this fix.

@Apyrael
Apyrael commented Feb 14, 2017

Tested 1.7.0RC2 and it is fixed. This issue gave me headaches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment