使用Reflections google库的单元测试仅在Maven执行时失败

Ser*_*gio 7 java eclipse classloader maven-3 reflections

我正在使用Google Reflections库来查询类路径中的某些资源.这些资源与我项目中的类位于同一位置.

我写了一些单元测试,这些测试在Eclipse中作为单元测试执行时成功,但是当我尝试用Maven执行它们时(maven install例如),它们没有按预期工作.经过一些调试,显然问题是当使用Maven执行时,Reflections库找不到资源所在的类路径url.

我得出结论,研究Reflection如何确定应检查的类路径URL.作为一个例子,下面的方法显示了Reflection如何在给定类加载器的情况下找到可用的类路径URL(原始的Reflection方法已经简化了一点):

public static Set<URL> forClassLoader(ClassLoader... classLoaders) {
    final Set<URL> result = Sets.newHashSet();
    for (ClassLoader classLoader : classLoaders) {
        while (classLoader != null) {
            if (classLoader instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) classLoader).getURLs();
                if (urls != null) {
                    result.addAll(Sets.<URL>newHashSet(urls));
                }
            } 
            classLoader = classLoader.getParent();
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

简而言之,它遍历类加载器层次结构,询问每个单独的类加载器的URL.

在Eclipse中,我使用类似这样的单元测试调用前一个方法:

    ClassLoader myClassClassLoader = <MyClass>.class.getClassLoader(); //<MyClass> is in the same classpath url than the resources I need to find
    Set<URL> urls = forClassLoader(myClassClassLoader);
    for(URL url : urls) {
      System.out.println("a url: " + url);
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,我可以看到(在许多其他URL中)作为项目的一部分配置的类路径URL:

file:<MY_PROJECT_PATH>/target/classes/
file:<MY_PROJECT_PATH>/target/test-classes/
Run Code Online (Sandbox Code Playgroud)

和思考作为一种魅力(思考应找到的资源位于其中file:<MY_PROJECT_PATH>/target/classes/).

但是,当测试由Maven执行时,我意识到forClassLoader方法返回的集合中缺少这些URL条目,并且其余的Reflections方法对此问题没有按预期工作.

"令人惊讶"的是,如果我在maven执行单元测试时写这个:

ClassLoader myClassClassLoader = <MyClass>.class.getClassLoader();
url = myClassClassLoader.getResource("anExistingResource");
System.out.println("URL: "+url); //a valid URL
Run Code Online (Sandbox Code Playgroud)

我可以看到类加载器仍然可以解析我想要找到的资源.我很困惑为什么当使用Maven执行时,forClassLoader方法不会在返回的集合中包含我的项目的类路径URL,尽管它同时能够解析位于这些URL中的资源(!).

这种行为的原因是什么?是否有任何解决方法我可以尝试使作为由Maven运行的单元测试的一部分调用的Reflections库工作?

Ser*_*gio 6

解决了它.发布解决方案,以防将来有人发现同样的问题.

在执行项目的单元测试时,Maven不会(明确地)在类路径中包含其所有依赖项.相反,它声明了对位于"target/surefire/surefirebooter_NUMBER_THAT_LOOKS_LIKE_TIME_STAMP.jar"中的tmp jar的依赖.此jar只包含一个声明项目类路径的清单文件.

forClassLoaderReflections库中的方法不会返回一组具有有效类路径的URL (即,清除清单文件中的类路径条目).为了解决这个问题,我刚刚实现了这个简单的方法

public static Set<URL> effectiveClassPathUrls(ClassLoader... classLoaders) {
    return ClasspathHelper.forManifest(ClasspathHelper.forClassLoader(classLoaders));
}
Run Code Online (Sandbox Code Playgroud)

该方法forManifest(也反思库的一部分),增加了一组被作为参数,在包含在集合中的任何jar文件清单文件中声明丢失的类路径条目的类路径的URL.通过这种方式,该方法返回一组带有项目有效类路径的URL.

  • 对,或者你可以尝试我的解决方案.看看链接.尝试在surefire插件中添加useManifestOnlyJar = false并使用SystemClassLoader = true. (4认同)

ccl*_*eve 3

您可能正在使用 M2Eclipse,它会自行将内容添加到类路径中。命令行 Maven 的工作方式有所不同。您可能会发现一些有帮助的选项