Quarkus 测试类加载的内部(和一些外部)已更改

有什么变化?

Quarkus 测试类加载的内部在 3.22 版本中被重写了。这不会影响生产模式和开发模式,也不会影响某些 Quarkus 测试模式,例如 @QuarkusIntegrationTest@QuarkusComponentTest。然而,@QuarkusTest 发生了变化。这次更改应该能让 Quarkus 测试工作得更好,并且使我们能够修复大量长期存在的 bug。它还将使我们能够改进与 Pact 等测试框架的集成。然而,它也引入了一些我们已知以及很可能我们尚未发现的 bug。我们热切希望获得社区的反馈,以便我们能够着手修复。

为什么?

在以前的版本中,Quarkus 测试是使用默认的 JUnit 类加载器调用的,然后在不同的、Quarkus 感知的类加载器中执行。

这在大多数情况下都运行得非常好,并且意味着 QuarkusTest 测试在很大程度上表现得就像它们是待测试代码的同一应用程序的一部分一样。Quarkus 测试框架可以在测试生命周期的正确节点启动和停止 Quarkus 实例,注入 CDI 依赖项,并执行其他有用的 Quarkus 字节码操作。但是,某些用例不起作用。使用高级 JUnit 5 功能(如 @TestTemplate@ParameterizedTest)的测试有时会发现相同的测试代码在单个测试中似乎在多个类加载器中运行,或者注入的依赖项并不总是可用。

虽然 Quarkus 扩展可以执行各种出色的字节码操作来改善开发人员体验,但它们不能像操作普通应用程序类那样自由地操作测试类。

随着时间的推移,与测试相关的缺陷不断累积,如果没有对 Quarkus 加载和执行测试的方式进行根本性重写,就无法改变它们。Quarkus 测试代码本身也变得越来越复杂,因为它试图规避各种 JUnit 的边缘情况。将测试实例从一个类加载器移动到另一个类加载器涉及序列化和反序列化,这对于类安全控制更严格的新 JVM 版本来说更难实现。例如,Quarkus 以前使用 XStream 作为序列化提供程序,但由于新 JVM 的反射限制,XStream 不再支持 Java 17 及更高版本。

如果 Quarkus 测试只是在其加载它们的类加载器中运行,那会怎么样?

你需要做什么

从 Quarkus 3.22 开始,@QuarkusTest 类加载正是这样工作的。你的测试需要进行哪些更改才能与新架构协同工作?**(希望)不需要**!

此次更改的目标之一是重写不触及我们测试套件中的任何测试,以确保它们都能继续工作而无需更新。实际上,已经有一些小故障,我们也发现了一些更广泛生态系统中的边缘情况。

已知的回归

  • 所有开发服务现在都在 JUnit 发现阶段启动Quarkus Dev Services 目前在 增强阶段启动,与字节码操作和其他应用程序初始化步骤一起。有了新的测试设计,所有增强都在测试运行开始时,在 JUnit 发现阶段进行。这意味着所有开发服务也在测试运行开始时启动。如果在运行任何测试之前,多个具有不同开发服务配置的测试类被增强,则可能同时运行多个配置不同的开发服务。这可能导致端口冲突和配置值交叉干扰。我们希望在下一个版本中 修复此问题。作为一种临时解决方法,将有冲突的测试拆分到不同的项目中可以解决这些症状。

  • 从 JUnit 条件访问配置。从自定义 JUnit 条件中使用 ConfigProvider触发 ServiceConfigurationError。解决方法是在读取配置之前将线程上下文类加载器设置为 this.getClass().getClassLoader(),然后再将其恢复。此问题已在 3.23 中修复。

  • 嵌套测试问题。如果嵌套的 @QuarkusTest 测试与普通测试在同一项目中混合,则普通测试 将无法访问 Quarkus 配置,因为线程上下文类加载器没有正确重置。作为一种临时解决方法,您可以在普通测试中手动将线程上下文类加载器设置为系统类加载器。嵌套测试在开发模式下也 存在问题。其中大多数问题已在 3.23 中修复,其余情况已在 3.24 中解决。

  • junit-platform.properties 在项目中包含 junit-platform.properties 文件会 给使用 @QuarkusTest 的测试带来问题。此问题已在 3.24 中修复,但请注意,junit-platform.properties 文件也可能 干扰多配置文件测试。要注册类排序器,最好在 Quarkus 应用程序属性中 配置排序器

  • Gradle 源集。在某些情况下,一个 Gradle 源集中的类 无法访问另一个源集中的包私有字段和类。这会导致 IllegalAccessError。解决方法是将包私有更改为公共。此问题已在 3.24 中修复。

  • IDE 支持。从 Eclipse IDE 运行 QuarkusTest 测试 更具挑战性。将类作为 JUnit 测试运行会产生错误。作为临时解决方法,您可以单独运行测试方法,或者运行整个包,或者将 -uniqueId [engine:junit-jupiter]/[class:<your.class.name.here>] 添加到运行配置的程序参数中。此问题已在 Eclipse 4.37 中修复。同样,在 Visual Studio Code 中,运行类或包中的所有测试 会失败,但运行单个测试方法可以正常工作。

  • 运行测试时内存占用增加。 对于使用多个配置文件和资源的套件,可能需要更多的堆或元空间。

需要注意的事项

  • 测试顺序更改。作为重写的一部分,某些测试的执行顺序已经发生了变化。当然,我们都知道测试如果不显式设置顺序就不应该依赖于执行顺序。然而,很容易忽略某个测试需要特定顺序……直到顺序改变。我们发现我们自己的套件中有一些测试对执行顺序很敏感,其他人也可能发现类似的情况。

  • 测试计时更改。我们还发现重写暴露了一些测试中的计时问题。由于类加载是在测试运行开始时预先加载的,而不是在测试执行之间加载,因此在测试之间异步操作完成的时间更少。例如,在下一个测试开始之前,外部状态可能不再有时间“重置”。这可能会在测试套件中暴露一些难以捉摸的 bug。

已停止支持

  • @Nested 测试上的 @TestProfile 混合不同的测试配置文件和测试资源在 @Nested 测试上不再受支持。根据定义,每个 @TestProfile 都必须获得自己的 Quarkus 应用程序和类加载器。让多个类加载器执行一个测试与使用用于运行它的类加载器加载该测试不兼容。

  • Maven Surefire 插件 2.x 版本。Maven Surefire 插件 3.x 以下版本将不再支持 @QuarkusTest。Surefire 插件 3 版本于 2023 年发布,因此 2 版本现在相当过时。

  • 并行测试执行。 Quarkus 测试的并行执行 从未得到支持,但在某些情况下可行。现在它不太可能工作。

后续步骤

测试类加载重写的主要工作已在 3.22 版本中完成,并为许多可能的改进铺平了道路。一些测试缺陷没有被主要更改直接修复,但现在的架构已准备好进行修复。更令人兴奋的是,与测试相关的扩展(如 Pact 扩展)现在可以添加新功能来减少测试的样板代码。

一如既往,如果您发现任何问题或异常,请在 zulip 上告知我们,或 提出问题。用于测试类加载的 工作组仍在进行中,并欢迎贡献。