使用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中的类加载器问题。
您可以创建一些问题,以确保失败。
-
有一个命名惯例; 测试套件应该被称为’TestBlaBla’或’BlaBlaTest’; 以“Test”开头或结尾。
-
如前所述,Maven中的类路径比Eclipse中的类路径更受限制,因为Eclipse(愚蠢地)不会将编译类路径与测试类路径分开。
-
Surefire可以任意顺序从不同的测试套件中运行测试用例。 当运行多个初始化一些公共库(例如内存数据库或JNDI上下文)的测试套件时,这些测试套件可能会在测试套件开始相互影响的情况下产生冲突。 您需要注意正确隔离测试套件。 我使用的技巧是为套件使用单独的内存数据库,并且每个unit testing初始化共享内容而不是每个测试套件。
3是我能告诉你最难调试的; 每当某些东西在Eclipse中运行而不在Maven中我自然会认为我在隔离测试套件时做错了什么。