如何动态检测 JUnit 测试?
How to dynamically instrument JUnit tests?
我正在使用 Invesdwin (https://github.com/subes/invesdwin-instrument) 将 java 代理动态加载到我的 Spring Boot 项目中的主要方法中,在上下文启动之前:
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
ApplicationContext springContext = SpringApplication.run(Some_Service.class);
...
这很好用,因为它消除了在从命令行 运行ning java -jar 命令时添加 -javaagent 参数的需要。
问题出现在单元测试中。因为他们没有 main 方法(我可以利用它),所以我无法在 Spring 上下文初始化之前使这两行 运行 。如果没有这些参数,每个测试都会导致上下文加载失败并出现此错误:
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 中的单个测试方法将失败,除非我手动将这些参数添加到该测试的 运行 配置中,这至少可以说是一种痛苦。
我做了一个自定义 运行ner class 试图在 Spring 上下文初始化之前制作代码 运行,像这样:
public class WeavingRunner extends SpringJUnit4ClassRunner
{
public WeavingRunner(Class<?> clazz) throws InitializationError
{
super(clazz);
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
}
}
尽管 Eclipse 控制台确实提示我在我的基础测试使用此 运行ner class 而不是 SpringRunner 时正在发生编织,但我得到了各种编织似乎表明动态编织还没有足够快地发生的错误:
java.lang.NoSuchMethodException: com.something.SomeAspectClass.aspectOf()
在 运行ning JUnit 测试时,是否有一种已知的方法可以在 main 方法中复制代码 运行ning?
******编辑******
我注意到一些非常奇怪的事情。如果我 运行 将包含测试的包作为 JUnit 测试,它就可以工作!仅当 运行将 src/test/java 文件夹作为 Eclipse 中的 JUnit 测试或 运行将应用程序本身作为 JUnit 测试(我最终需要)时,才会出现上述编织错误。动态编织正在工作,但不知何故使得它仅在 运行 单独测试或作为 JUnit 测试的封闭包时才工作。我希望这是有道理的!
我开始怀疑我的 aop.xml 文件有问题,但如果 运行 单个测试甚至整个包都工作正常,那怎么会是问题呢?!
JUnit 可能会进行 class路径扫描以发现单元测试。在测试中调用 invesdwin-instrument 之前,还将加载 junit classes 及其所有依赖项。因此单元测试class本身不能使用方面。我知道的唯一解决方法是将方面用法放入嵌套 class 中,该嵌套 class 在初始化测试 class 后加载,从而加载 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
我正在使用 Invesdwin (https://github.com/subes/invesdwin-instrument) 将 java 代理动态加载到我的 Spring Boot 项目中的主要方法中,在上下文启动之前:
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
ApplicationContext springContext = SpringApplication.run(Some_Service.class);
...
这很好用,因为它消除了在从命令行 运行ning java -jar 命令时添加 -javaagent 参数的需要。
问题出现在单元测试中。因为他们没有 main 方法(我可以利用它),所以我无法在 Spring 上下文初始化之前使这两行 运行 。如果没有这些参数,每个测试都会导致上下文加载失败并出现此错误:
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 中的单个测试方法将失败,除非我手动将这些参数添加到该测试的 运行 配置中,这至少可以说是一种痛苦。
我做了一个自定义 运行ner class 试图在 Spring 上下文初始化之前制作代码 运行,像这样:
public class WeavingRunner extends SpringJUnit4ClassRunner
{
public WeavingRunner(Class<?> clazz) throws InitializationError
{
super(clazz);
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
}
}
尽管 Eclipse 控制台确实提示我在我的基础测试使用此 运行ner class 而不是 SpringRunner 时正在发生编织,但我得到了各种编织似乎表明动态编织还没有足够快地发生的错误:
java.lang.NoSuchMethodException: com.something.SomeAspectClass.aspectOf()
在 运行ning JUnit 测试时,是否有一种已知的方法可以在 main 方法中复制代码 运行ning?
******编辑******
我注意到一些非常奇怪的事情。如果我 运行 将包含测试的包作为 JUnit 测试,它就可以工作!仅当 运行将 src/test/java 文件夹作为 Eclipse 中的 JUnit 测试或 运行将应用程序本身作为 JUnit 测试(我最终需要)时,才会出现上述编织错误。动态编织正在工作,但不知何故使得它仅在 运行 单独测试或作为 JUnit 测试的封闭包时才工作。我希望这是有道理的!
我开始怀疑我的 aop.xml 文件有问题,但如果 运行 单个测试甚至整个包都工作正常,那怎么会是问题呢?!
JUnit 可能会进行 class路径扫描以发现单元测试。在测试中调用 invesdwin-instrument 之前,还将加载 junit classes 及其所有依赖项。因此单元测试class本身不能使用方面。我知道的唯一解决方法是将方面用法放入嵌套 class 中,该嵌套 class 在初始化测试 class 后加载,从而加载 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