How to test a ClassFileTransformer / javaagent?
Asked Answered
O

1

6

I implemented a ClassFileTransformer for a javaagent using ASM. Because it has some bugs, I want to write a JUnit test case for it. How do I do this?

Using pseudo-code I thought along the lines:

// Have a test class as subject
public static class Subject {
  public void doSomething(){...}
}
// Manually load and transform the subject
...?
// Normally execute some now transformed methods of the subject
new Subject().doSomething();
// Check the result of the call (i.e. whether the correct attached methods were called)
Assert.assertTrue(MyClassFileTransformer.wasCalled());

Now the question is: How do I manually load and transform the subject and make the JVM/Classloader use my manipulated version of it? Or do I completely miss something?

Offset answered 5/7, 2011 at 17:42 Comment(0)
O
8

I got it. One needs to implement an own ClassLoader that does the same transformation with the test subject as the ClassFileTransformer (e.g. calls it). And of course the subject class may not already be loaded, so there may not be any direct usage of it. So I used Java reflection API to execute the methods of the subject class.

In a separate file:

public static class Subject {
    public void doSomething(){...}
}

In the test:

private static class TransformingClassLoader extends ClassLoader {

    private final String className;

    public TransformingClassLoader(String className) {
        super();
        this.className = className;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (name.equals(className)) {
            byte[] byteBuffer = instrumentByteCode(fullyQualifiedSubjectClass);
            return defineClass(className, byteBuffer, 0, byteBuffer.length);
        }
        return super.loadClass(name);
    }
}

@Test
public void testSubject(){
    ClassLoader classLoader = new TransformingClassLoader(fullyQualifiedSubjectClass);
    Class<?> subjectClass = classLoader.loadClass(fullyQualifiedSubjectClass);
    Constructor<?> constructor = subjectClass.getConstructor();
    Object subject = constructor.newInstance();
    Method doSomething = subjectClass.getMethod("doSomething");
    doSomething.invoke(subject);
    Assert.assertTrue(MyClassFileTransformer.wasCalled());
}
Offset answered 7/7, 2011 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.