使用Reflections google库的unit testing仅在Maven执行时失败

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

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

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

 public static Set forClassLoader(ClassLoader... classLoaders) { final Set 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.newHashSet(urls)); } } classLoader = classLoader.getParent(); } } return result; } 

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

在Eclipse中,我使用类似这样的unit testing调用前一个方法:

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

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

 file:/target/classes/ file:/target/test-classes/ 

和Reflections作为一个魅力(Reflections应该找到的资源位于file:/target/classes/ )。

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

令人惊讶的是,如果我在maven执行unit testing时写这个:

 ClassLoader myClassClassLoader = .class.getClassLoader(); url = myClassClassLoader.getResource("anExistingResource"); System.out.println("URL: "+url); //a valid URL 

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

这种行为的原因是什么? 有什么解决方法我可以尝试在作为Maven运行的unit testing的一部分调用时使Reflections库工作吗?

你可能正在使用M2Eclipse,它自己在类路径中添加了东西。 命令行Maven的工作方式不同。 您可能会发现一些有用的选项 。

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

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

Reflections库中的forClassLoader方法不会返回一组带有效类路径的url(即forClassLoader manifest文件中的类路径条目)。 为了解决这个问题,我刚刚实现了这个简单的方法

 public static Set effectiveClassPathUrls(ClassLoader... classLoaders) { return ClasspathHelper.forManifest(ClasspathHelper.forClassLoader(classLoaders)); } 

forManifest (也是Reflections库的一部分)的方法添加到作为参数发送的类路径URL集合中,在集合中包含的任何jar文件的清单文件中声明的缺少的类路径条目。 通过这种方式,该方法返回一组带有项目有效类路径的URL。

我有同样的问题。 添加以下URL对我来说是个窍门。

 ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setUrls(...); cb.addUrls(YourClassName.class.getProtectionDomain().getCodeSource().getLocation()); 

我刚刚遇到了与Reflections库(版本0.9.11)相同的问题,只有在从Maven构建执行unit testing时才会遇到。 接受的答案中提供的链接指向了正确的方向。

一个简单的POM文件更改为我的Surefire插件修复了这个问题:

   org.apache.maven.plugins maven-surefire-plugin 2.21.0  false    maven-failsafe-plugin 2.21.0  false     integration-test verify     

配置参数默认为“true”。 强制它为’false’似乎解决了我的unit testing中的类加载器问题。

您可以创建一些问题,以确保失败。

  1. 有一个命名惯例; 测试套件应该被称为’TestBlaBla’或’BlaBlaTest’; 以“Test”开头或结尾。

  2. 如前所述,Maven中的类路径比Eclipse中的类路径更受限制,因为Eclipse(愚蠢地)不会将编译类路径与测试类路径分开。

  3. Surefire可以任意顺序从不同的测试套件中运行测试用例。 当运行多个初始化一些公共库(例如内存数据库或JNDI上下文)的测试套件时,这些测试套件可能会在测试套件开始相互影响的情况下产生冲突。 您需要注意正确隔离测试套件。 我使用的技巧是为套件使用单独的内存数据库,并且每个unit testing初始化​​共享内容而不是每个测试套件。

3是我能告诉你最难调试的; 每当某些东西在Eclipse中运行而不在Maven中我自然会认为我在隔离测试套件时做错了什么。