I am using Invesdwin (https://github.com/subes/invesdwin-instrument) to dynamically load java agents into my Spring Boot project in the main method, before the context starts :
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
ApplicationContext springContext = SpringApplication.run(Some_Service.class);
...
This works great because it eliminates the need for adding -javaagent parameters when running the java -jar command from the command line.
The issue arises when it comes to unit tests. Since they don't have a main method (that I can tap into that is), I cannot make those 2 lines run before the Spring Context initializes. Without those arguments, each test will cause the context to fail to load with this error :
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
I can work around this during the final build by setting up the Surefire plugin this way in my POM :
<!--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>
Unfortunately this only works during the final build phase. Running an individual test method in Eclipse will fail unless I manually add those arguments into the run configuration for that test which is a pain to say the least.
I made a custom runner class in an attempt to make code run before the Spring context initializes, like so :
public class WeavingRunner extends SpringJUnit4ClassRunner
{
public WeavingRunner(Class<?> clazz) throws InitializationError
{
super(clazz);
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
}
}
Although the Eclipse console does give me hints that the weaving is happening when my base test uses this runner class instead of SpringRunner, I get all sorts of weaving errors that seem to indicate the dynamic weaving hasn't happened soon enough :
java.lang.NoSuchMethodException: com.something.SomeAspectClass.aspectOf()
Is there a known way to replicate code running in a main method when running JUnit tests?
******EDIT******
I noticed something extremely weird with this. If I run the package enclosing the tests as a JUnit test, it works! The above weaving errors only appear when running the src/test/java folder as a JUnit test in Eclipse or running the app itself as a JUnit test (which I ultimately need). The dynamic weaving is working but somehow something is making it only work when running individual tests or up to the enclosing package as a JUnit test. I hope that makes sense!
I was starting to suspect issues with my aop.xml file but how could that be the issue if running individual tests and even entire packages works fine?!
JUnit might do classpath scanning to discover unit tests. Also junit classes with all their dependencies will be loaded before invesdwin-instrument can be invoked inside the test. Thus the unit test class itself can not use aspects. The only workaround I know for this is to put the aspect usage into a nested class which gets loaded after the test class is initialized and thus invesdwin-instrument is loaded. An example for this pattern where @Transactional aspects are tested is here: 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