如何动态检测 JUnit 测试?
Posted
技术标签:
【中文标题】如何动态检测 JUnit 测试?【英文标题】:How to dynamically instrument JUnit tests? 【发布时间】:2020-06-15 09:10:30 【问题描述】:在上下文开始之前,我正在使用 Invesdwin (https://github.com/subes/invesdwin-instrument) 在 main 方法中将 java 代理动态加载到我的 Spring Boot 项目中:
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
ApplicationContext springContext = SpringApplication.run(Some_Service.class);
...
这很有效,因为它消除了在从命令行运行 java -jar 命令时添加 -javaagent 参数的需要。
当涉及到单元测试时,就会出现问题。由于它们没有 main 方法(我可以利用它),因此我无法在 Spring Context 初始化之前使这两行运行。如果没有这些参数,每个测试都会导致上下文加载失败并出现此错误:
ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:spring-instrument-version.jar
我可以在最终构建期间通过在我的 POM 中设置 Surefire 插件来解决此问题:
<!--Maven Surefire Plugin-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<argLine>
-javaagent:lib/aspectjweaver-1.9.5.jar
-javaagent:lib/spring-instrument-5.2.3.RELEASE.jar
</argLine>
</configuration>
</plugin>
不幸的是,这仅在最终构建阶段有效。在 Eclipse 中运行单个测试方法将失败,除非我手动将这些参数添加到该测试的运行配置中,这至少可以说是一件痛苦的事情。
我创建了一个自定义运行器类,试图让代码在 Spring 上下文初始化之前运行,如下所示:
public class WeavingRunner extends SpringJUnit4ClassRunner
public WeavingRunner(Class<?> clazz) throws InitializationError
super(clazz);
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
虽然当我的基础测试使用此运行器类而不是 SpringRunner 时,Eclipse 控制台确实提示我正在发生编织,但我得到了各种编织错误,这些错误似乎表明动态编织还没有很快发生:
java.lang.NoSuchMethodException: com.something.SomeAspectClass.aspectOf()
在运行 JUnit 测试时,是否有一种已知的方法可以复制在 main 方法中运行的代码?
******编辑******
我注意到这很奇怪。如果我将包含测试的包作为 JUnit 测试运行,它就可以工作!仅当在 Eclipse 中将 src/test/java 文件夹作为 JUnit 测试运行或将应用程序本身作为 JUnit 测试运行(我最终需要)时,才会出现上述编织错误。动态编织正在工作,但不知何故,它只在运行单个测试或作为 JUnit 测试的封闭包时才起作用。我希望这是有道理的!
我开始怀疑我的 aop.xml 文件有问题,但如果运行单个测试甚至整个包都可以正常工作,这怎么可能是问题?!
【问题讨论】:
我知道 Eclipse 中的 AspectJ 支持 (AJDT) 在大多数方面都比 IntelliJ IDEA 更好,但是仍然存在问题,并且由于缺乏人力而缺乏维护。我通常静态编译我的方面或使用编织代理,而不是动态编织器附件,因为它对类加载的顺序很敏感。但是您可以尝试编写自己的检测 JUnit 运行器。无论如何,为了重现和分析您的问题,MCVE 会很好,最好是在 GitHub 上。我们最终可能会得到解决方法和/或 Eclipse 错误票。 我上面的 WeavingRunner 课程是一个(糟糕的)尝试编写我自己的仪器运行程序的尝试,但在某些情况下某些事情在时间方面表现不佳。我将尝试整理一个完整的示例。 【参考方案1】:JUnit 可能会进行类路径扫描以发现单元测试。此外,在可以在测试中调用 invesdwin-instrument 之前,将加载具有所有依赖项的 junit 类。因此单元测试类本身不能使用方面。我知道的唯一解决方法是将方面使用放入一个嵌套类中,该类在初始化测试类并因此加载 invesdwin-instrument 后加载。这里有一个测试@Transactional 方面的模式示例:https://github.com/invesdwin/invesdwin-context-persistence/blob/master/invesdwin-context-persistence-parent/invesdwin-context-persistence-jpa-hibernate/src/test/java/de/invesdwin/context/persistence/jpa/hibernate/MultiplePersistenceUnitsTest.java
【讨论】:
以上是关于如何动态检测 JUnit 测试?的主要内容,如果未能解决你的问题,请参考以下文章
当 pom.xml 中存在 TestContainers 依赖项时,未检测到 JUnit 5 测试
我可以同时运行@TestFactory生成的Junit动态测试吗?