Search code examples
javaeclipsejuniteclipse-plugineclipse-pde

JUnitCore giving "No runnable methods" error in Eclipse plugin


I am trying to write an Eclipse plugin that can run JUnit tests and do something with the results. My plugin loads a given class correctly, but fails to run the JUnit tests and gives an error: initializationError(className): No runnable methods. When I run the test class using Result result = JUnitCore.runClasses(className.class); Failure failure : result.getFailures(); from within the same Eclipse instance, however, I don't get any errors.

I think my problem is the one that @gubby describes in the question java.lang.Exception: No runnable methods exception in running JUnits, but I don't know how to implement his suggestion to a solution which reads: "Solution is to load JUnitCore in the same ClassLoader as the tests themselves."

Here is a reduced version of my implementation (please assume that everything except the loading of the runnable methods work):

ClassLoader classLoader = ClassLoaderHelper.getClassLoader(FileFinder.getCurrentProject());
Class clazz = classLoader.loadClass(fileName.substring(0, fileName.indexOf(".class")));
Result result = JUnitCore.runClasses(clazz);
Failure failure : result.getFailures()

The code to get the ClassLoader is the following:

public static ClassLoader getClassLoader(IProject project) {
    String[] classPathEntries = null;
    try {
        project.open(null);
        IJavaProject javaProject = JavaCore.create(project);
        classPathEntries = JavaRuntime.computeDefaultRuntimeClassPath(javaProject);
    } catch (CoreException e1) {
        e1.printStackTrace();
    }

    List<URL> urlList = new ArrayList<URL>();
    for (String entry : classPathEntries) {
        IPath path = new Path(entry);
            URL url;
            try {
                url = path.toFile().toURI().toURL();
                urlList.add(url);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
    }
    ClassLoader parentClassLoader = project.getClass().getClassLoader();
    URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]);
    URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader);

    return classLoader;
}

Solution

  • For Eclipse plugins, you have basically two options to have one plugin share a class loader with another.

    1. Buddy Class Loading. Note that this breaks loose coupling, but it's easy to "implement" as you simply add two statements in the two respective plugins' MANIFEST.MF and an export statement as well. The following rules apply (from the link given above).

      • The bundle Y must specify the registered buddy policy (i.e. Eclipse-BuddyPolicy: registered)

      • The bundle X must specify the symbolic name of Y in the Eclipse-RegisterBuddy header (i.e Eclipse-RegisterBuddy: Y)

      • The bundle X must be dependent on a package exported by bundle Y. This can happen through either a Require-Bundle or Import-Package constraint.

    2. Fragments: You can "attach" a fragment to a plugin. Both share the same class loader. Usually, this technique is used for things like plugin i18n, but it's also the recommended practice for adding unit tests to plugins. This way, the tests don't have to go into the same plugin, and possibly unneeded test classes or dependencies won't go in the production code.

      There's a wizard for fragments in Eclipse, but they're basically plugins themselves, which declare a "host plugin".

    So, you could consider putting your code into a fragment and attach that to the respective plugin containing the code-under-test. Or, if you need to reuse your code for different plugins and do not care about loose coupling, use Buddy Class Loading.

    Also, check whether you have all the right dependencies in the plugin (e.g., org.junit). You need the JUnit contained in the Java Development Tools.