如何动态检测 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