生成的代码测试包含PowerMock

时间:2012-05-22 03:53:52

标签: java compiler-construction code-generation classloader powermock

我最近碰到了一个障碍。我正在写一些单元测试来练习我的库的一些部分负责代码生成。代码接受配置对象的输入,使用CodeModel编写代码,将这些类转储到String,然后使用Java 6编译器api将它们动态编译为可用的类。

我正在尝试的障碍是我在生成的代码中测试的方法之一使用带有final方法的类(Android Bundle)并在使用时抛出异常(" java.lang.RuntimeException:Stub!&# 34)。因此,为了解决这个问题,我使用PowerMock来模拟最终方法。但是,在这种情况下,编译器API会抛出NPE。我认为这是因为PowerMock在幕后使用的设施,但我不确定。

这是例外:

at org.androidtransfuse.gen.classloader.MemoryFileManager.<init>(MemoryFileManager.java:11)
at org.androidtransfuse.gen.classloader.MemoryClassLoader.<init>(MemoryClassLoader.java:16)
at org.androidtransfuse.gen.ParcelableGeneratorTest.setup(ParcelableGeneratorTest.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:129)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:93)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)

MemoryFileManager.java:

class MemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {
    public final Map<String, Output> map = new HashMap<String, Output>();

    MemoryFileManager(JavaCompiler compiler) {
        super(compiler.getStandardFileManager(null, null, null));
    }

    @Override
    public Output getJavaFileForOutput
            (Location location, String name, JavaFileObject.Kind kind, FileObject source) {
        Output mc = new Output(name, kind);
        this.map.put(name, mc);
        return mc;
    }
}

MemoryClassLoader.java(使用MemoryFileManager):

public class MemoryClassLoader extends ClassLoader {
    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    private final MemoryFileManager manager = new MemoryFileManager(this.compiler);

    public void add(String classname, String filecontent) {
        add(Collections.singletonMap(classname, filecontent));
    }

    public void add(Map<String, String> map) {
        List<Source> list = new ArrayList<Source>();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            list.add(new Source(entry.getKey(), JavaFileObject.Kind.SOURCE, entry.getValue()));
        }
        this.compiler.getTask(null, this.manager, null, null, null, list).call();
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        synchronized (this.manager) {
            Output mc = this.manager.map.remove(name);
            if (mc != null) {
                byte[] array = mc.toByteArray();
                return defineClass(name, array, 0, array.length);
            }
        }
        return super.findClass(name);
    }
}

这是一个非常基本的测试,证明了这个问题:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Parcel.class)
public class ExampleTest {

    private static final String TEST_VALUE = "test value";

    String classToCompile =
            "import android.os.Parcel;\n" +
            "\n" +
            "public class ClassToCompile\n" +
            "{\n" +
            "\n" +
            "    private Parcel parcel;\n" +
            "}";

    @Test
    public void test() throws ClassNotFoundException, IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        MemoryClassLoader classLoader = new MemoryClassLoader();
        classLoader.add("ClassToCompile", classToCompile);
        classLoader.loadClass("ClassToCompile");
    }
}

1 个答案:

答案 0 :(得分:0)

Johan on the Powermock Google Group answered这对我来说,但我想我也会在这里添加结果。

对我来说有用的是从@RunWith(PowerMockRunner.class)方法改为here所描述的Java代理方法。

这要求我删除@RunWith注释并将以下内容添加到我的Maven POM中:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>
            -javaagent:${settings.localRepository}/org/powermock/powermock-module-javaagent/1.4.12/powermock-module-javaagent-1.4.12.jar
        </argLine>
    </configuration>
</plugin>

当在IDE中运行单元测试时,我必须添加

-javaagent: <jarpath>/powermock-module-javaagent-1.4.12.jar

运行配置。