JiangYongGang / powermock

Automatically exported from code.google.com/p/powermock
Apache License 2.0
0 stars 0 forks source link

Unable to use Powermock agent approach with Robolectric #470

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Create a test that uses RobolectricTestRunner
2. Setup Powermock agent as described in the guide in this site.
3.

What is the expected output? What do you see instead?
A runtime error happens due to a class collision between the asm library that 
Robolectric depends on and the one shipped with the powermock javaagent library.

java.lang.IncompatibleClassChangeError: class org.objectweb.asm.tree.ClassNode 
has interface org.objectweb.asm.ClassVisitor as super class

What version of the product are you using? On what operating system?
Robolectic 2.2, Powermock 1.5.1, Mac OS X Lion

Please provide any additional information below.

More info here 
http://stackoverflow.com/questions/19998398/class-loading-collision-between-robo
lectric-and-powermock

Original issue reported on code.google.com by davidst@gmail.com on 15 Nov 2013 at 10:13

GoogleCodeExporter commented 9 years ago
Additional StackOverflow question: http://stackoverflow.com/q/20557769/16487

Original comment by C.Ross.E...@gmail.com on 17 Dec 2013 at 2:38

GoogleCodeExporter commented 9 years ago
Thanks, please report back here if you find any work-arounds.

Original comment by johan.ha...@gmail.com on 18 Dec 2013 at 3:41

GoogleCodeExporter commented 9 years ago
I can get the rule mode to to work using the following code, but I then get 
IllegalAccessError whenever MainActivity or TestMainActivity tries to access an 
anonymous inner class (i.e. MainActivity$1 or TestMainActivity$1). I have to 
refactor the code to avoid anonymous inner classes to get it to run.

@PrepareForTest(fullyQualifiedNames = { "application.package.MainActivity" })
@PowerMockIgnore({ "*" })
@RunWith(RobolectricTestRunner.class)
public class TestMainActivity {
    @Rule
    public PowerMockRule rule = new PowerMockRule();

Original comment by goo...@sa.me.uk on 5 Jun 2014 at 7:03

GoogleCodeExporter commented 9 years ago
I can't easily find a more restrictive @PowerMockIgnore() list. It needs to 
include org.mockito.* and org.powermock.* but it starts breaking static fields 
in Robolectric (specifically, org.robolectric.Robolectric.application is null 
during the test run).

When org.robolectric.* is excluded I get a LinkageError:
java.lang.LinkageError: loader constraint violation: when resolving field 
"application" the class loader (instance of 
org/powermock/core/classloader/MockClassLoader) of the referring class, 
org/robolectric/Robolectric, and the class loader (instance of 
org/robolectric/bytecode/AsmInstrumentingClassLoader) for the field's resolved 
type, bytecode/AsmInstrumentingClassLoader, have different Class objects for 
that type

If I specifically include the $1, $2, etc. anonymous classes in @PrepareForTest 
then I can use them in the test without an IllegalAccessError.

Original comment by goo...@sa.me.uk on 5 Jun 2014 at 6:40

GoogleCodeExporter commented 9 years ago
The following works and I don't have any issues with anonymous classes:

@RunWith(RobolectricTestRunner.class)
@PrepareForTest(fullyQualifiedNames = { "application.package.MainActivity" })
@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
public class TestMainActivity {
    @Rule
    public PowerMockRule rule = new PowerMockRule();

The "org.mockito.*" exclusion is necessary or it fails to initialise.
The "org.robolectric.*" exclusion is necessary or 
org.robolectric.Robolectric.application will be null.
the "android.*" exclusion is necessary or there will be two different instances 
of the android.app.Application class present causing an error for the 
org.robolectric.Robolectric.application field.

Original comment by goo...@sa.me.uk on 6 Jun 2014 at 5:50

GoogleCodeExporter commented 9 years ago
I tried implementing the above, but I keep running into ClassCastExceptions 
where it cannot cast between Class A and Class A, but with the classloaders as 
org.powermock.core.classloader.MockClassLoader and
org.robolectric.bytecode.AsmInstrumentingClassLoader. 

I only have two classes listed in my @PrepareForTest, but the PowerMock 
classloader seems to be loading everything in the class under test.

Original comment by C.Ross.E...@gmail.com on 9 Jun 2014 at 12:42

GoogleCodeExporter commented 9 years ago
Class B is referencing Class A and one of them is excluded, you need to exclude 
the other one with @PowerMockIgnore too. I'm using the rule+xstream method not 
the agent (which fails to work at all because it rejects the system class 
loader).

Original comment by goo...@sa.me.uk on 9 Jun 2014 at 12:59