简体   繁体   English

Maven Surefire 测试中的 ClassNotFoundExceptions

[英]ClassNotFoundExceptions in Maven Surefire tests

While executing tests in Maven Surefire I see ClassNotFoundException s from time to time.在 Maven Surefire 中执行测试时,我不时看到ClassNotFoundException

This really gives me a headache, since:这真的让我很头疼,因为:

  • the missing classes vary.缺失的课程各不相同。 Only around 5 classes are affected, but which one it is varys from build to build.只有大约 5 个类受到影响,但哪个类因构建而异。 However, I see no unique similarities between these classes, which they wouldn't share with 20 other classes of the same kind.但是,我看不出这些类之间有什么独特的相似之处,它们不会与 20 个其他同类类共享。
  • These missing classes come from 2 different dependencies.这些缺失的类来自 2 个不同的依赖项。 These are managed by Maven, of course.当然,这些是由 Maven 管理的。
  • When a CNFE is raised I had a look at the class path (during runtime!) and it looks fine!当提出CNFE ,我查看了类路径(在运行时!),看起来不错!

How I analysed the class path我如何分析类路径

I took the code of the "class path scanner" from Arno Haase:我从 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: URL 列表很短:

  • a few JRE libs一些 JRE 库
  • a "surefire booter jar"一个“万无一失的引导罐”

The latter jar bundles all my Maven dependencies in its Manifest file, as described in the Surefire docs.后一个 jar 将我所有的 Maven 依赖项捆绑在其 Manifest 文件中, 如 Surefire 文档中所述。

So I dug further and analysed the "Class-Path" attribute of the manifest.所以我进一步挖掘并分析了清单的“Class-Path”属性。 There I found the dependent jar listed, where the missing class should have come from.在那里我找到了列出的依赖 jar,缺少的类应该来自哪里。 When browsing through the jar's entries, I also found the missing class there.在浏览 jar 的条目时,我还在那里找到了丢失的类。 The fully qualified path also matches.完全限定的路径也匹配。

So in principle everything seems to be correct and in place.所以原则上一切似乎都是正确和到位的。 Where should I continue to investigate now?我现在应该在哪里继续调查?

There are several things to check for problems like these.有几件事可以检查这些问题。

  • Does this happen from command line or via CI build only?这是从命令行发生的还是仅通过 CI 构建发生的? If using Jenkins or Hudson, is this a Maven project or a FreeStyle project with a Maven build step?如果使用 Jenkins 或 Hudson,这是 Maven 项目还是带有 Maven 构建步骤的 FreeStyle 项目? If this is a Maven project, switch it to a FreeStyle project with a Maven build step, and that just may solve the issue.如果这是一个 Maven 项目,将它切换到一个带有 Maven 构建步骤的 FreeStyle 项目,这可能会解决问题。 Stephen Connolly of the Maven team considers the Jenkins Maven build type evil . Maven 团队的 Stephen Connolly 认为Jenkins Maven 构建类型是 evil
  • Ensure there is only one version of each dependency and that related dependencies (Spring, ASM, Hibernate, etc.) have the same/compatible versions.确保每个依赖项只有一个版本,并且相关依赖项(Spring、ASM、Hibernate 等)具有相同/兼容的版本。 Pay particular attention to artifacts where the group ID or artifact ID has changed, for example spring.jar vs. spring-core.jar .请特别注意组 ID 或工件 ID 已更改的工件,例如spring.jarspring-core.jar The old Tattletale plugin might be useful to get started.旧的Tattletale 插件可能对开始有用。
  • Replace any dependencies ending in -all with their component parts.将所有以 -all 结尾的依赖项替换为其组成部分。 -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. - 所有 jars 可能包含运行库所需的每个类 - 重新打包到 jar 文件中,Maven 的依赖项解析过程无法获取它们 - 而不是将它们作为依赖项引用。 mockito-all, hamcrest-all, powermock-all, cglib are examples. mockito-all、hamcrest-all、powermock-all、cglib 都是例子。
  • If using coverage tools (Jacoco, Clover) does the build work if you turn off the coverage?如果使用覆盖工具(Jacoco、Clover),如果关闭覆盖,构建是否有效? If yes, the tool may be introducing classpath jars that conflict with your app's.如果是,则该工具可能会引入与您的应用程序冲突的类路径 jar。 (Different version of CGLIB for example.) Run in debug mode and compare dependencies with/without coverage to identify the differences. (例如,CGLIB 的不同版本。)在调试模式下运行并比较有/无覆盖的依赖关系以识别差异。
  • If using JUnit, make sure Maven surefire is using the right JUnit provider for your version of JUnit.如果使用 JUnit,请确保 Maven surefire 为您的 JUnit 版本使用正确的 JUnit 提供程序。 Run the build in debug mode with -X (redirect output to a file if using command line).使用-X在调试模式下运行构建(如果使用命令行,则将输出重定向到文件)。 Grep the output for surefire-junit . Grep 输出surefire-junit You should find something like this:你应该找到这样的东西:

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

    Now, make sure the version of the provider matches the version of JUnit used.现在,确保提供程序的版本与使用的 JUnit 版本匹配。 Check out the Maven docs for information on which provider to use and how to configure.查看Maven 文档以获取有关使用哪个提供程序以及如何配置的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM