使用 Maven 构建单独的 JAR 文件以对自定义类加载器进行单元测试
-
05-07-2019 - |
题
作为我当前项目的一部分,我创建了一个自定义类加载器。自定义加载程序的部分单元测试涉及使用一些 JAR 文件来演示加载程序的正确行为。
我想在运行实际的单元测试之前从 Java 源构建测试 JAR 文件。此外,运行单元测试时,测试 JAR 文件不能位于类路径上,因为我想在测试执行期间动态加载它们。
是否有一个标准模式来完成这种“在测试阶段之前构建一些 JAR,但将它们排除在类路径之外”的要求?我不敢相信我是第一个尝试使用 Maven 2 执行此操作的人,但我似乎无法找到正确的 POM 结构和依赖项。通常我最终会在测试阶段之前没有构建一些测试 jar,但我也遇到了构建顺序不一致的问题,导致构建可以在一台机器上正常工作,但无法构建一些在另一个上测试罐子。
解决方案
最简单的方法是设置另一个项目来打包测试jar的类,然后将其设置为正常 test-scoped 依赖。
如果您不想/不能这样做,您可以使用程序集插件在 process-test-classes
阶段创建一个jar(即测试完成后)编译但在测试执行之前)。下面的配置将调用程序集插件,以在目标目录的该阶段创建名为 classloader-test-deps
的jar。然后,您的测试可以根据需要使用该jar。
程序集插件使用程序集描述符(在src / main / assembly中,称为test-assembly.xml)来打包target / test-classes的内容。我已经设置了一个过滤器来包含com.test包及其子项的内容。这假设你有一些包名称约定,你可以申请jar的内容。
默认情况下,程序集插件将jar作为附加工件附加,通过将 attach
指定为false,它将不会被安装/部署。
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-2</version>
<executions>
<execution>
<id>create-test-dependency</id>
<phase>process-test-classes</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>classloader-test-deps</finalName>
<attach>false</attach>
<descriptors>
<descriptor>src/main/assembly/test-assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
这是test-assembly.xml的内容
<assembly>
<id>test-classloader</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.testOutputDirectory}</directory>
<outputDirectory>/</outputDirectory>
<!--modify/add include to match your package(s) -->
<includes>
<include>com/test/**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
其他提示
我会尝试在测试中设置您的测试所需的一切。主要优点是测试中没有隐含的神奇的看不见的设置。该测试可以在每个环境中运行。此外,添加新的严格隔离的场景要容易得多,因为您不依赖于某些混合场景设置。
设置不应该太难:
- 序列化一个java类:
- 带有一些类型代码工程库
- 或者,使用重命名为 .class 以外的某个文件后缀的 java 类文件。将其放在测试资源文件夹下并使用类加载器加载(getResourceAsStream(...))。
- 压缩类文件(`java.util.zip.GZIPOutputStream`)
- 使用类加载器加载类文件
还有一种替代方法,它使用 java 类加载器设计,并且无需生成其他类即可工作。
Java 有一个类加载器层次结构。每个类加载器都有一个父类加载器。类加载器层次结构的根是引导类加载器。当一个类被类加载器加载时,它会尝试先用父类加载器加载该类,然后再加载它自己。
您可以使用当前的类加载器加载测试类。将其打包并使用您自己的类加载器加载它。唯一的区别是您将父类加载器设置为无法加载测试类的父类加载器。
String resource = My.class.getName().replace(".", "/") + ".class";
//class loader of your test class
ClassLoader myClassLoader = currentThread().getContextClassLoader();
assert ! toList(myClassLoader.getResources(resource)).isEmpty();
//just to be sure that the resource cannot be loaded from the parent classloader
ClassLoader parentClassloader = getSystemClassLoader().getParent();
assert toList(parentClassloader.getResources(resource)).isEmpty();
//your class loader
URLClassLoader myLoader = new URLClassLoader(new URL[0], parentClassloader);
assert toList(myLoader.getResources(resource)).isEmpty();
Maven通过依赖关系分析解析构建顺序,因此通常你的JAR会按顺序构建,因为使用你的测试JAR的JAR只会将它们声明为依赖关系。但是,依赖关系也放在类路径上。 “范围”依赖关系确定它继续的类路径。例如,'compile'依赖项在类路径上进行编译,测试和运行; 'runtime'依赖项在类路径上进行测试和运行; 'test'依赖项仅在测试期间在类路径上。不幸的是,你有一个案例没有被任何可用范围覆盖:你有一个依赖,但你不希望它在类路径上。这是一个边缘用例,也是您在发现示例时遇到问题的原因。
所以,除非一些Maven大师重新表明相反,否则我建议如果不编写特殊的Maven插件,这是不可能的。不过,我推荐别的东西。你真的需要定制的JAR来测试你的类加载器吗?这对我来说听起来很可疑。也许你可以使用任何旧的JAR?如果是这样,我将使用maven-dependency-plugin将一些已知的JAR复制到您的本地模块的目标目录中(例如log4j)。然后,您的测试可以通过 target / log4j-xxx.jar
上的文件路径访问该JAR,您可以做任何事情。