使用Junit的Javassist API

时间:2016-12-09 14:35:36

标签: 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,但是,我必须承认我不确定如何使用不同的类池来解决问题。

3 个答案:

答案 0 :(得分:1)

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

为了让你的单元测试通过,我不得不:

  1. 创建我自己的临时ClassLoader,该some.package.Class将能够加载javassist.MyClass(由ClassLoader替换以用于测试目的)并且将以首次尝试的方式实施在父母的CL。
  2. 之前从中加载课程
  3. 将我自己的ClassLoader设置为上下文SimulatedSession#performThis()
  4. 更改internalClass.defrost()的代码,以便能够获取此方法创建的类实例,并调用doThis()以阻止" 冻结类问题& #34;
  5. 通过反射调用方法SimulatedSession#performThis(),以确保通过使用ClassLoader返回的类实例确保我有不同的输出,以确保使用的类已加载javassist.MyClass
  6. 假设我的班级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
    

    <强>输出:

    import numpy as np
    import seaborn as sns
    import matplotlib.pyplot as plt
    from sklearn import cross_validation
    from sklearn.linear_model import LogisticRegression
    sns.set()
    
    x = (np.random.randint(2000, size=400)).reshape((400,1))
    y = (np.random.randint(2, size=400)).reshape((400,1)).ravel()
    
    x_train, x_test, y_train, y_test = cross_validation.train_test_split(x, y, test_size=0.4, random_state=0)
    
    logistic_regr = LogisticRegression()
    logistic_regr.fit(x_train, y_train)
    
    fig, ax = plt.subplots()
    
    ax.set(xlabel='x', ylabel='y')
    ax.plot(x_test, logistic_regr.predict_proba(x_test), label='Logistic regr')
    #ax.plot(x_test,logistic_regr.predict(x_test), label='Logistic regr')
    ax.legend()
    

答案 1 :(得分:1)

看看retransformer。这是我用于运行测试的基于Javassist的lib。它比使用原始Javassist更简洁。

答案 2 :(得分:0)

也许是另一种方法。我们遇到了类似的问题,因为我们曾经嘲笑过依赖 - 我们无法重置它。所以我们做了以下几点:在每次测试之前,我们更换了&#39; live&#39;我们的模拟实例。测试后,我们恢复实时实例。因此,我建议您为每个测试替换第三方代码的修改实例。

@Before
public void setup()
{
    this.liveBeanImpl = (LiveBean) ReflectionTools.getFieldValue(this.beanToTest, "liveBean");
    ReflectionTools.setFieldValue(this.beanToTest, "liveBean", new TestStub());
}


@After
public void cleanup()
{
    ReflectionTools.setFieldValue(this.beanToTest, "liveBean", his.liveBeanImpl);
}

setFieldValue如下所示:

public static void setFieldValue(Object instanceToModify, String fieldName, Object valueToSet)
{
    try
    {
        Field declaredFieldToSet = instanceToModify.getClass().getDeclaredField(fieldName);
        declaredFieldToSet.setAccessible(true);
        declaredFieldToSet.set(instanceToModify, valueToSet);
        declaredFieldToSet.setAccessible(false);
    }
    catch (Exception exception)
    {
        String className = exception.getClass().getCanonicalName();
        String message = exception.getMessage();
        String errorFormat = "\n\t'%s' caught when setting value of field '%s': %s";
        String error = String.format(errorFormat, className, fieldName, message);
        Assert.fail(error);
    }
}

因此,如果重置每个测试的实现,可能会通过测试。你明白了吗?