Peter Wippermann
Peter Wippermann

Reputation: 4589

ClassNotFoundExceptions in Maven Surefire tests

While executing tests in Maven Surefire I see ClassNotFoundExceptions from time to time.

This really gives me a headache, since:

How I analysed the class path

I took the code of the "class path scanner" from Arno Haase:

public List<URL> getRootUrls () {
  List<URL> result = new ArrayList<> ();

  ClassLoader cl = Thread.currentThread().getContextClassLoader();
  while (cl != null) {
    if (cl instanceof URLClassLoader) {
      URL[] urls = ((URLClassLoader) cl).getURLs();
      result.addAll (Arrays.asList (urls));
    }
    cl = cl.getParent();
  }
  return result;
}

The list of URLs is quite short:

The latter jar bundles all my Maven dependencies in its Manifest file, as described in the Surefire docs.

So I dug further and analysed the "Class-Path" attribute of the manifest. There I found the dependent jar listed, where the missing class should have come from. When browsing through the jar's entries, I also found the missing class there. The fully qualified path also matches.

So in principle everything seems to be correct and in place. Where should I continue to investigate now?

Upvotes: 1

Views: 154

Answers (1)

user944849
user944849

Reputation: 14951

There are several things to check for problems like these.

  • Does this happen from command line or via CI build only? If using Jenkins or Hudson, is this a Maven project or a FreeStyle project with a Maven build step? If this is a Maven project, switch it to a FreeStyle project with a Maven build step, and that just may solve the issue. Stephen Connolly of the Maven team considers the Jenkins Maven build type evil.
  • Ensure there is only one version of each dependency and that related dependencies (Spring, ASM, Hibernate, etc.) have the same/compatible versions. Pay particular attention to artifacts where the group ID or artifact ID has changed, for example spring.jar vs. spring-core.jar. The old Tattletale plugin might be useful to get started.
  • Replace any dependencies ending in -all with their component parts. -all jars may contain every class needed to run the library - repackaged into the jar file where Maven's dependency resolution process can't get at them - instead of referencing them as dependencies. mockito-all, hamcrest-all, powermock-all, cglib are examples.
  • If using coverage tools (Jacoco, Clover) does the build work if you turn off the coverage? If yes, the tool may be introducing classpath jars that conflict with your app's. (Different version of CGLIB for example.) Run in debug mode and compare dependencies with/without coverage to identify the differences.
  • If using JUnit, make sure Maven surefire is using the right JUnit provider for your version of JUnit. Run the build in debug mode with -X (redirect output to a file if using command line). Grep the output for surefire-junit. You should find something like this:

    [DEBUG] org.apache.maven.surefire:surefire-junit4:jar:2.16:test (selected for test)

    Now, make sure the version of the provider matches the version of JUnit used. Check out the Maven docs for information on which provider to use and how to configure.

Upvotes: 1

Related Questions