java项目中所有类的单元测试可序列化性

java项目中所有类的单元测试可序列化性,java,unit-testing,serialization,Java,Unit Testing,Serialization,我在java项目中有数千个类。其中一些实现了可序列化接口。现在有个问题。有人可能会进入一个类,添加一个既不是暂时的也不是可序列化的新变量。代码编译得很好,但这个过程在运行时会崩溃 为了说明这一点 class Foo实现可序列化的{....//all good} 类Foo实现可序列化 { //哦,executorService不可序列化。它也不声明为瞬态 私有执行器服务执行器服务=。。 } 我正在考虑编写一个单元测试,它将遍历所有类并确保“真正的可序列化性”。我读过一些关于序列化特定对象的讨

我在java项目中有数千个类。其中一些实现了可序列化接口。现在有个问题。有人可能会进入一个类,添加一个既不是暂时的也不是可序列化的新变量。代码编译得很好,但这个过程在运行时会崩溃

为了说明这一点

class Foo实现可序列化的{....//all good}
类Foo实现可序列化
{  
//哦,executorService不可序列化。它也不声明为瞬态
私有执行器服务执行器服务=。。
}
我正在考虑编写一个单元测试,它将遍历所有类并确保“真正的可序列化性”。我读过一些关于序列化特定对象的讨论。我理解这个过程,但它需要

1) 正在创建对象。
2) 序列化,然后
3) 反序列化

是否有更有效和实用的方法。也许要用反思。遍历所有类,若类具有可序列化性,则所有属性都必须是可序列化的或具有临时关键字


想法?

如果序列化是应用程序的关键部分,那么在测试中包括序列化。比如:

@Test
public void aFooSerializesAndDeserializesCorrectly {
    Foo fooBeforeSerialization = new Foo();
    ReflectionUtils.randomlyPopulateFields(foo);
    Foo fooAfterSerialization = Serializer.serializeAndDeserialize(foo);
    assertThat(fooAfterSerialization, hasSameFieldValues(fooBeforeSerialization));
}
编辑:一个简单的
随机流行字段实现

public static void randomlyPopulateFields(final Object o) {
    ReflectionUtils.doWithFields(o.getClass(), new ReflectionUtils.FieldCallback() {
        public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
            ReflectionUtils.makeAccessible(field);
            ReflectionUtils.setField(field, o, randomValueFor(field.getType()));
        }

        private Random r = new Random();
        private Object randomValueFor(Class<?> type) {
            if (type == String.class) {
                return String.valueOf(r.nextDouble());
            } else if (type == Boolean.class || type == Boolean.TYPE) {
                return r.nextBoolean();
            } else if (type == Byte.class || type == Byte.TYPE) {
                return (byte) r.nextInt();
            } else if (type == Short.class || type == Short.TYPE) {
                return (short) r.nextInt();
            } else if (type == Integer.class || type == Integer.TYPE) {
                return r.nextInt();
            } else if (type == Long.class || type == Long.TYPE) {
                return (long) r.nextInt();
            } else if (Number.class.isAssignableFrom(type) || type.isPrimitive()) {
                return Byte.valueOf("1234");
            } else if (Date.class.isAssignableFrom(type)) {
                return new Date(r.nextLong());
            } else {
                System.out.println("Sorry, I don't know how to generate values of type " + type);
                return null;
            }
        }
    });
}
publicstaticvoid随机填充字段(最终对象o){
ReflectionUtils.doWithFields(o.getClass(),新ReflectionUtils.FieldCallback()){
public void doWith(Field)抛出IllegalArgumentException、IllegalAccessException{
ReflectionUtils.MakeAccessable(字段);
ReflectionUtils.setField(field,o,randomValueFor(field.getType());
}
私有随机r=新随机();
私有对象randomValueFor(类类型){
if(type==String.class){
返回字符串.valueOf(r.nextDouble());
}else if(type==Boolean.class | | type==Boolean.type){
返回r.nextBoolean();
}else if(type==Byte.class | | type==Byte.type){
返回(字节)r.nextInt();
}else if(type==Short.class | | type==Short.type){
返回(短)r.nextInt();
}else if(type==Integer.class | | type==Integer.type){
返回r.nextInt();
}else if(type==Long.class | | type==Long.type){
返回(长)r.nextInt();
}else if(Number.class.isAssignableFrom(type)| | type.isPrimitive()){
返回字节.valueOf(“1234”);
}else if(Date.class.isAssignableFrom(type)){
返回新日期(r.nextLong());
}否则{
System.out.println(“对不起,我不知道如何生成“+type”类型的值);
返回null;
}
}
});
}
1) 创建一个对象。2) 序列化,然后反序列化

这份清单不完整;您还需要初始化。考虑这个例子:

class CanBeSerialized implements Serializable {
    private String a; // serializable
    private Thread t; // not serializable
}

class CannotBeSerialized implements Serializable {
    private String a;                // serializable
    private Thread t = new Thread(); // not serializable
}
您可以序列化和反序列化第一个,但在第二个上会得到
notserializableeexception
。更复杂的是,如果使用了接口,您永远无法判断类是否会通过序列化,因为流式传输的是该接口后面的类的具体对象:

class PerhapsCanBeSerializedButYouNeverKnow implements Serializable {
    private Runnable r; // interface type - who knows?
}
前提是,您可以保证您的所有类和您的类使用的类将被测试:

  • 存在默认构造函数
  • 字段中没有接口类型
然后可以通过反射自动创建和初始化它们,然后测试序列化。但那真的很难,不是吗?否则,正确的初始化取决于手动操作

您可以以不同的方式使用反射:迭代要检查的
对象列表,获取它们的
字段[]
,并验证它们是否是瞬态的(
Field.getModifiers()
),或者它们是否直接实现了
可序列化的
Field.getType().getInterfaces()
)或间接地(通过超级接口或类)。此外,考虑您要检查的深度,取决于序列化机制的工作深度。 正如Ryan正确指出的,如果代码足够邪恶,那么静态序列化检查将失败:

class SeeminglySerializable implements Serializable {
    // ...
        private void writeObject/readObject() {
             throw new NotSerializableException();
        }
}

或者如果
readObject()/writeObject()
的实现不好。要针对此类问题进行测试,您需要实际测试序列化过程,而不是它背后的代码。

您指的是什么
ReflectionUtils
?没有特别的。我想我在某个图书馆的某个地方见过这样的实用程序,但我通常只写我自己的。任何创建适当填充对象的方法都可以。关键是,如果要测试序列化,则需要序列化对象。问题是,如果不提供有关如何初始化对象的元数据,则无法轻松实现此类方法。这就是为什么我发现你的答案有误导性,因为它欺骗了看似简单的问题解决方案。添加了一个简单的实现。最糟糕的是必须单独处理所有数字类型。这并不难。这也是为测试获取填充对象的唯一可能方法。实际的方法取决于您已经建立了什么样的测试基础设施。太好了。现在你清楚地表明你的方法背后没有魔法,只有努力工作。你还没有解释魔法。如果使用了这些规则,你们列出的任何一条规则都不会适用。@RyanStewart我想说的是,我的算法只是一个近似值,但它让我滑倒了。非常感谢。