我最近遇到了一个障碍。我正在编写一些单元测试来运行我的库的一些部分,负责代码生成。代码接受配置对象的输入,使用CodeModel编写代码,将这些类转储为String,然后使用Java 6编译器API将它们实时编译为可用的类。生成代码测试合并PowerMock
我碰到的障碍是我在生成的代码中测试的方法之一,它使用带有最终方法(Android Bundle)的类,并在使用时抛出异常(“java.lang.RuntimeException:Stub!”)。所以,为了解决这个问题,我使用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");
}
}