[powermock] PowerMock and JMX

3,437 views
Skip to first unread message

ryanobjc

unread,
May 4, 2010, 10:14:07 PM5/4/10
to PowerMock
Hi guys,

Doing some testing on a class that has a simple JMX initializer like
so:

final MBeanServer mbs =
ManagementFactory.getPlatformMBeanServer();

ObjectName name = new ObjectName("hbase:" +
"service=Thrift,name=" + Thread.currentThread().getName());

mbs.registerMBean(this, name);


When I run my test I get an error:
java.lang.LinkageError: loader constraint violation: loader (instance
of org/powermock/core/classloader/MockClassLoader) previously
initiated loading for a different type with name "javax/management/
MBeanServer"

This exception line comes from the "ObjectName name = ..." line...
whoa!

So I decide I can be clever in the test set-up:
MBeanServer mbeanMock = PowerMockito.mock(MBeanServer.class);
PowerMockito.mockStatic(ManagementFactory.class);

when(ManagementFactory.getPlatformMBeanServer()).thenReturn(mbeanMock);

PowerMockito.whenNew(ObjectName.class).withArguments(any())
.thenReturn(null); // null object


So this looks ok - we replace the mbs with a mock which noops, also we
return a bogus ObjectName that is null. Essentially the entire section
of code is faked (in the original listing).

But now I get a very mysterious error:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
MBeanServer$$EnhancerByMockitoWithCGLIB$$215731b3 cannot be returned
by getPlatformMBeanServer()
getPlatformMBeanServer() should return MBeanServer


I run the debugger, and we are ultimately failing here:
AnswersValidator.java:57:
if (!answer.returnsNull() && !
invocation.isValidReturnType(answer.getReturnType())) {

reporter.wrongTypeOfReturnValue(invocation.printMethodReturnType(),
answer.printReturnType(), invocation.getMethodName());
}

The call invocation.isValidReturnType returns false. The method looks
like so:
public boolean isValidReturnType(Class clazz) {
if (method.getReturnType().isPrimitive()) {
return Primitives.primitiveTypeOf(clazz) ==
method.getReturnType();
} else {
return method.getReturnType().isAssignableFrom(clazz);
}
}

Inside the debugger we take the second path. clazz is a CGLIB mocked
up MBeanServer mock that in it's interface list implements
javax.management.MBeanServer. method.getReturnType() is Class for
javax.management.MBeanServer. This call 'isAssignableFrom' returns
false. That is the mock is apparently not assignable to the class.

This simple unit test:
MBeanServer mbeanMock = Mockito.mock(MBeanServer.class);
Class<MBeanServer> clazz = MBeanServer.class;
System.out.println(clazz.isAssignableFrom(mbeanMock.getClass()));

prints 'true'.

I made another unit test that isolates these things:
@RunWith(PowerMockRunner.class)
@PrepareForTest( { ManagementFactory.class })
public class TestRyan extends TestCase {

public void testSimple() throws Exception {
MBeanServer mbeanMock = PowerMockito.mock(MBeanServer.class);

PowerMockito.mockStatic(ManagementFactory.class);

when(ManagementFactory.getPlatformMBeanServer()).thenReturn(mbeanMock);
}
}

and this fails with and without the @PrepareForTest line. With the
line I get the WrongTypeOfReturnValue. Without the line I get a
different kind of error.


As for my environment... I am running Java6 with IntelliJ on my mac.
I'll try a linux machine asap. My classpath is pretty big, and it
includes some usual suspects (its the classpath of the Apache HBase
project FYI):
- commons-logging-api-1.0.4
- log4j-1.2.15
- all the mockito and powermock dependencies
- many many more.

I also looked at http://code.google.com/p/powermock/wiki/MockSystem
indicating that I should PrepareForTest the calling class (I am but I
dont even get that far) and includes an easyMock style code snippet.
Could the problem be related to my use of the Mockito interface of
Powermock? Should I try easyMock?


My initial problem was "how do I run mockito on a class that also
initializes a JMX mbean/dynamic bean". I would still like to answer
that one - the only hits I get with the error message come from my own
project's JIRA indicating that PowerMock doesnt work for the
aforementioned reason.

--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To post to this group, send email to powe...@googlegroups.com.
To unsubscribe from this group, send email to powermock+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/powermock?hl=en.

ryanobjc

unread,
May 5, 2010, 11:04:56 PM5/5/10
to PowerMock
I solved my own problem by using:

@PowerMockIgnore*( {"javax.management.*"})

In the mean time I am running into other issues. The basic scenario
is I am doing a powermock-mediated test of various HBase things. These
tests also spin up complex threads and other code to run an entire
test suite in-memory. I'd like to use powermock to let me control
some certain aspects of the code (eg: inject faults, etc). But I keep
running in to a never-ending series of bizzare errors, eg:

java.lang.RuntimeException: java.lang.RuntimeException: class
org.apache.hadoop.net.StaticMapping not
org.apache.hadoop.net.DNSToSwitchMapping

In this case the calling code is expecting what is returned to be able
to be casted to DNSToSwitchMapping, but it isnt. Needless to say the
definition of "StaticMapping" in the class files does implement that
interface.

But before then I ran into a series of exceptions that necessitated me
to do such:
@PowerMockIgnore( {
"org.apache.commons.logging.*",
"javax.management.*",
"javax.xml.*",
"com.sun.org.apache.xerces.*",
"org.w3c.*"
})

And the list grows!

This doesn't seem to be a tenable approach to using powermock to help
me improve the testability of my "legacy" code.

What am I doing wrong here?
> I also looked athttp://code.google.com/p/powermock/wiki/MockSystem

Johan Haleby

unread,
May 6, 2010, 2:34:03 AM5/6/10
to powe...@googlegroups.com
Hi, 

I don't think you're doing anything wrong, the problem is that when using some frameworks that make use of classloading or creating classes using reflection with specific classloaders you can run into problems like this when using PowerMock. This is a known and unfortunate side-effect. We have plans to create a java agent bootstrapper for PowerMock as well which would eliminate these problems but this not something that is a priority (if you're interested to help out in anyway you're most welcome). In the mean time the approach you'd have to take with these kinds of frameworks are the one that you're taking already, i.e. you need to ignore stuff for test or remove (static) initializers of the frameworks where the initialization code takes place. It's not easy and I would suggest you to try to look at alternatives, for example introduce an anti corruption layer in your code and mock that using standard EasyMock/Mockito instead.

/Johan
Reply all
Reply to author
Forward
0 new messages