Java 用void方法模拟原因';本地类名与流类名“不兼容”;“无效”';通过Spark进行反序列化/序列化时

Java 用void方法模拟原因';本地类名与流类名“不兼容”;“无效”';通过Spark进行反序列化/序列化时,java,apache-spark,mockito,Java,Apache Spark,Mockito,尝试使用Spark为应用程序创建测试时,我会遇到以下错误: java.io.InvalidClassException: java.lang.Void; local class name incompatible with stream class name "void" at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:620) at java.io.ObjectInputStream.readNo

尝试使用Spark为应用程序创建测试时,我会遇到以下错误:

java.io.InvalidClassException: java.lang.Void; local class name incompatible with stream class name "void"
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:620)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
    at java.io.ObjectInputStream.readClass(ObjectInputStream.java:1678)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2245)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2169)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2027)
只有在模拟某些类时才会发生这种情况,这些类具有
void
方法,这些方法将在测试单元运行期间的某个时间点调用

例如,我的代码是:

public class MyTest {

    private MyClass uut;

    private Writer writer;

    @Captor
    private ArgumentCaptor<Dataset<Row>> rowCaptor;

    @Before
    public void setUp() {
        initMocks(this);

        writer = mock(Writer.class);
        uut = new MyClass(writer);
    }

    @Test
    public void testSomething() {
        // given

        // when
        uut.process();

        // then
        verify(writer, times(2)).write(rowCaptor.capture());
        List<Dataset<Row>> result = rowCaptor.getAllValues();
        // ...
    }
}
公共类MyTest{
私有MyClass uut;
私人作家;
@俘虏
私人辩手,辩手;
@以前
公共作废设置(){
initMocks(这个);
writer=mock(writer.class);
uut=新的MyClass(编写器);
}
@试验
公共空间{
//给定
//什么时候
uut.process();
//然后
验证(writer,times(2)).write(rowCaptor.capture());
List result=rowCaptor.getAllValues();
// ...
}
}

问题似乎在于Mockito序列化其内部代理类的方式。只有在spark中运行的任务/作业实际被序列化和反序列化时,才会产生负面影响

org.apache.spark.scheduler.ShuffleMapTask#runTask
中,任务被反序列化。Spark在这一点上的基本功能是:

new JavaDeserializationStream(new ByteBufferInputStream(ByteBuffer.wrap(this.taskBinary.value())), ClassLoader.getSystemClassLoader()).objIn.readObject()
这将生成与vs完全相同的错误消息

new ObjectInputStream(new ByteArrayInputStream(this.taskBinary.value())).readObject()
这将正常工作并正确解析对象

特别是,Java/Spark期望
void
方法被序列化的方式与Mockito实际执行的方式之间似乎不匹配:
“Java.lang.void”
“void”与
“void”

幸运的是,Mockito允许您指定序列化其模拟的方式:

MockSettings mockSettings = Mockito.withSettings().serializable(SerializableMode.ACROSS_CLASSLOADERS);
writer = mock(Writer.class, mockSettings);
在这一变化之后,测试应该会起作用



请注意,例如,
verify
调用很棘手/如果模拟被序列化、发送到某处、反序列化然后再次使用,则不会像预期的那样工作。原始
编写器将看不到对mock的调用

问题似乎在于Mockito序列化其内部代理类的方式。只有在spark中运行的任务/作业实际被序列化和反序列化时,才会产生负面影响

org.apache.spark.scheduler.ShuffleMapTask#runTask
中,任务被反序列化。Spark在这一点上的基本功能是:

new JavaDeserializationStream(new ByteBufferInputStream(ByteBuffer.wrap(this.taskBinary.value())), ClassLoader.getSystemClassLoader()).objIn.readObject()
这将生成与vs完全相同的错误消息

new ObjectInputStream(new ByteArrayInputStream(this.taskBinary.value())).readObject()
这将正常工作并正确解析对象

特别是,Java/Spark期望
void
方法被序列化的方式与Mockito实际执行的方式之间似乎不匹配:
“Java.lang.void”
“void”
“void”

幸运的是,Mockito允许您指定序列化其模拟的方式:

MockSettings mockSettings = Mockito.withSettings().serializable(SerializableMode.ACROSS_CLASSLOADERS);
writer = mock(Writer.class, mockSettings);
在这一变化之后,测试应该会起作用


请注意,例如,
verify
调用很棘手/如果模拟被序列化、发送到某处、反序列化然后再次使用,则不会像预期的那样工作。原始
编写器将看不到模拟上的调用