4

I am writing JUnit-Tests in Eclipse for testing a RCP-Eclipse-Application without Maven. For some Testcases it is necessary to user Powermock (with Easymock) to mock object-creations and static methods.

I have a plugin containing the class which shall be testet and a second plugin containing the corresponding test. Let's donate them PluginObject and PluginTest:

Working Situation:

* PluginObject
  - src 
      - ClassUnderTest

* PluginTest
 - src
     - TestCase
 - lib
     - easymock
     - powermock
     - ... [aditional classes (like JavaAssist)]

With the Configuration with the powermock-Jar in the PLuginTest the TestCase and configured in the Plugins-Manifest-Runtim runs normal and returns the desired results.

My TestCase start with the following lines, currently using the @RunWith-Annotation:

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({ "javax.management.*", 
"javax.xml.parsers.*",
"com.sun.org.apache.xerces.internal.jaxp.*",
"javax.xml.*",
"ch.qos.logback.classic.*",
"org.slf4j.*",
"org.eclipse.core.runtime.jobs.ISchedulingRule" })
@PrepareForTest    ({Controller.class,ProcessLoaderFactory.class,Engine.class,CommandCollector.class})
@SuppressStaticInitializationFor({"Controller","   ProcessLoaderFactory","Engine","CommandCollector"})
public class ControllerTest {
...
[skipped package information due to copyrights]

But I have multiple plugins which want to use Powermock for mocking in my application, that it seems a good idea for me to extract the shared libraries to another special plugin - let's call it Test.Util:

* PluginObject
  - src 
      - ClassUnderTest

* PluginTest
 - src
     - TestCase

* Test.Util
 - lib
     - easymock
     - powermock
     - ... [aditional classes (like JavaAssist)]

For all other libraries except Powermock I have no problems but when I move Powermock from the PluginTest to the Test.Util-Plugin i get a "ClassNotFoundException" while initialization, when starting a JUnit PLugin-Test. (A normal JUnit-Test does not throw this error)

java.lang.ClassNotFoundException: xxx.yyy.zzz.MyTest
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:513)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:429)
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
at java.lang.ClassLoader.loadClass(Unknown Source)
at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:143)
at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:67)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:133)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:39)
at org.powermock.tests.utils.impl.AbstractTestSuiteChunkerImpl.createTestDelegators(AbstractTestSuiteChunkerImpl.java:217)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.<init>(JUnit4TestSuiteChunkerImpl.java:59)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.<init>(AbstractCommonPowerMockRunner.java:32)
at org.powermock.modules.junit4.PowerMockRunner.<init>(PowerMockRunner.java:31)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:31)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:24)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:33)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:48)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner.main(RemotePluginTestRunner.java:62)
at org.eclipse.pde.internal.junit.runtime.PlatformUITestHarness$1.run(PlatformUITestHarness.java:47)
at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:135)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4140)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3757)
at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2696)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2660)
at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2494)
at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:674)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:667)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:123)
at org.eclipse.pde.internal.junit.runtime.NonUIThreadTestApplication.runApp(NonUIThreadTestApplication.java:54)
at org.eclipse.pde.internal.junit.runtime.UITestApplication.runApp(UITestApplication.java:41)
at org.eclipse.pde.internal.junit.runtime.NonUIThreadTestApplication.start(NonUIThreadTestApplication.java:48)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:622)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:577)
at org.eclipse.equinox.launcher.Main.run(Main.java:1410)
at org.eclipse.equinox.launcher.Main.main(Main.java:1386)

I configured the Test.Util with the libraries in the Classpath under Runtime in the MANIFEST.MF and exported every package of these plugins. In Eclipse the correct Library is linked from the TestCase.

I also used Bootstrapping with the JUnit-Rule instead of @RunWith, but it lead to the same ClassNotFoundException. The Problem did not change.

I tried many things now - ReConfiguration of the Run-Configuration for the Plugin-Test, Setting the ClassPath: .\ in the Manifest, Lazy/Not-Lazy Loading and setting Buddy-ClassLoading for the PluginTest and the Test.Util-Plugin.

After hours of trying and searching on the web, I still have no clue why Powermock leads to this Exception if it is extracted from the Test-Plugin into a third plugin and how I can make this work. I can't currently say if I have something misconfigured in my application.

Anyone who has encountered the same or a similar problem or got a good solution for it?

Cyberfeaker
  • 41
  • 1
  • 3

3 Answers3

3

The "registered" policy did not help in my case, and I also did not want to register every plugin with the util plugin. The solution for me was in "Test.Util" to declare:

Eclipse-BuddyPolicy: dependent, registered, global, app

which makes more or less all classes available to PowerMockito.

Torsten
  • 31
  • 3
2

You need a Powermock version which has proper OSGI manifest and OSGI dependencies. Now you can give a try to my small project, which contains a P2 update site for Powermock 1.5.4. See https://code.google.com/p/powermock/ OSGI section (Direct link: https://code.google.com/p/powermock-osgi).

In an OSGI environment you can only manipulate classes with Powermock which are in exported packages. At my P2 update site you will find a Powermock, which has:

Eclipse-BuddyPolicy: global,ext,boot

This means basicly, that any exported package will be accessible to Powermock. Still you have to export the tested package. If you do not want to expose the package for all bundles, you can still use the X-Friends directive in your manifest. You export the package with Powermock as X-Friend, and thats all.

UPDATE: Answer for the question of Chriss:

Interesing, can you explain why PowerMock is only able to manipulate exported classes? My current understanding is that at runtime all packages are visible, only at build/compile time it matters.

If you use Class.forName() to get a class runtime in OSGI, you will use your bundle classloader to get the class for your name. This bundle classloader sees only classes, which are in imported packages. Powermock bundles have dynamic import, but still the classloader of the Powermock bundle will see only classes from any exported package of any bundle.

If you would know where your class comes from, which bundle exports it, then you would be able to fetch the classloader of that bundle, and from that classloader you could get the searched class. BUT this is of course quite complicated and it would mean, that Powermock bundle would need some hacky magic classloader, which hides this detail, but such thing does not exist.

For details see http://olegz.wordpress.com/2008/11/05/osgi-and-classforname/

UPDATE: Powermock-osgi is now hosted on github. https://github.com/liptga/powermock-osgi

Update site: https://raw.githubusercontent.com/liptga/powermock-osgi/master/update-site/1.5.6.0

Gábor Lipták
  • 9,646
  • 2
  • 59
  • 113
  • 1
    Interesing, can you explain why PowerMock is only able to manipulate exported classes? My current understanding is that at runtime all packages are visible, only at build/compile time it matters. – Chriss Feb 27 '14 at 10:10
  • 1
    can you take a look at this: http://stackoverflow.com/questions/22456767/noclassdeffounderror-org-hamcrest-matchers-using-powermock-osgi – Chriss Mar 17 '14 at 14:22
1

I have removed my previous comment as I sucessfully managed to run pde plugin test using the PowerMock libraries stored in a single plugin. to answer you question using your example, you need to add add the following line in your Test.Util (you should not use upercase for plugin id) MANIFEST.MF

Eclipse-BuddyPolicy: registered

Then if you are using PluginTest plugin is a plain plugin add the following line in it's MANIFEST.MF

Eclipse-RegisterBuddy: Test.Util

If your PluginTest is a fragment of the bundle PluginObject then you'll have to add the previous line in the host bundle that is PluginObject manifest. Hope this helps ;)