Javassist API 与 Junit

标签 java junit javassist

我试图在每次测试之前更改一些第三方类定义,以模拟不同的结果。我必须使用 javassist 之类的东西,因为有时由于访问修饰符的原因,扩展类是不可能的。以下是我尝试结合 javassist 和 junit 执行的操作的示例:

public class SimulatedSession extends SomeThirdParty {

    private boolean isJoe = false;
    public SimulatedSession(final boolean isJoe) {
        this.isJoe = isJoe;
    }

    @Override
    public void performThis() {
        final ClassPool classPool = ClassPool.getDefault();
        final CtClass internalClass = classPool.get("some.package.Class");
        final CtMethod callMethod = internalClass.getDeclaredMethod("doThis");
        if (isJoe) {
            callMethod.setBody("{System.out.println(\"Joe\");}");
        } else {
            callMethod.setBody("{System.out.println(\"mik\");}");
        }
        internalClass.toClass();
    }
}

@Test
public void firstTest() {
     SimulatedSession toUse = new SimulatedSession(false);
     // do something with this object and this flow
}

@Test
public void nextTest() {
     SimulatedSession toUse = new SimulatedSession(true);
     // do something with this object and this flow
}

如果我单独运行每个测试,我可以很好地运行代码。当我使用单元套件运行它们时,一个接一个的测试,我遇到了“卡住类问题”。为了解决这个问题,我正在查看这个post但是,我必须承认我不确定如何使用不同的类池来解决问题。

最佳答案

您当前的代码将尝试将两次相同的类加载到同一个 ClassLoader 中,这是禁止的,您只能为给定的 ClassLoader 加载一次类。

为了让你的单元测试通过,我必须:

  1. 创建我自己的临时ClassLoader,它将能够加载some.package.Class(我将其替换为javassist.MyClass以进行测试目的),并且将以这样的方式实现:它将首先尝试在父级 CL 之前从中加载类。
  2. 将我自己的 ClassLoader 设置为上下文 ClassLoader
  3. 更改 SimulatedSession#performThis() 的代码,以便能够获取由此方法创建的类实例,并调用 internalClass.defrost() 来防止“卡住类(class)问题”。
  4. 通过反射调用方法 doThis() 以确保我有不同的输出,方法是使用 SimulatedSession#performThis() 返回的类实例来确保使用的类已通过我的 ClassLoader 加载。

因此假设我的类 javassist.MyClass 是:

package javassist;

public class MyClass {
    public void doThis() {

    }
}

经过修改的方法SimulatedSession#performThis():

public Class<?> performThis() throws Exception {
    final ClassPool classPool = ClassPool.getDefault();
    final CtClass internalClass = classPool.get("javassist.MyClass");
    // Prevent the "frozen class issue"
    internalClass.defrost();
    ...
    return internalClass.toClass();
}

单元测试:

// The custom CL
private URLClassLoader cl;
// The previous context CL
private ClassLoader old;

@Before
public void init() throws Exception {
    // Provide the URL corresponding to the folder that contains the class
    // `javassist.MyClass`
    this.cl = new URLClassLoader(new URL[]{new File("target/classes").toURI().toURL()}){
        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
            try {
                // Try to find the class for this CL
                return findClass(name);
            } catch( ClassNotFoundException e ) {
                // Could not find the class so load it from the parent
                return super.loadClass(name, resolve);
            }
        }
    };
    // Get the current context CL and store it into old
    this.old = Thread.currentThread().getContextClassLoader();
    // Set the custom CL as new context CL
    Thread.currentThread().setContextClassLoader(cl);
}

@After
public void restore() throws Exception {
    // Restore the context CL
    Thread.currentThread().setContextClassLoader(old);
    // Close the custom CL
    cl.close();
}


@Test
public void firstTest() throws Exception {
    SimulatedSession toUse = new SimulatedSession(false);
    Class<?> c = toUse.performThis();
    // Invoke doThis() by reflection
    Object o2 = c.newInstance();
    c.getMethod("doThis").invoke(o2);
}

@Test
public void nextTest() throws Exception {
    SimulatedSession toUse = new SimulatedSession(true);
    Class<?> c = toUse.performThis();
    // Invoke doThis() by reflection
    Object o2 = c.newInstance();
    c.getMethod("doThis").invoke(o2);
}

输出:

mik
Joe

关于Javassist API 与 Junit,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41062755/

相关文章:

java - 负逻辑移位

eclipse - eclipse 项目中 junit 和 source 的两个独立 JRE

java - 重新定义从另一个编译方法调用的方法的方法签名

java - 模拟抽象类的其余部分但调用其中的真实方法?

java - 在 Java 中, boolean 运算还是整数运算更快?

java - 如何集成 Citrus 框架和 BDD Cucumber

android - Robolectric:actionBar.hide() 返回 Null

java - 创建以保留字为变量的动态类

java - 使用 cglib 或 javaassist 哪一个

java - 我如何摆脱 "MySQLSyntaxErrorException"