unit testingjava项目中所有类的可序列化

我的java项目中有数千个类。 其中一些实现了可序列化的接口。 现在这是一个问题。 有人可以进入一个类,添加既不是瞬态也不可序列化的新变量。 代码编译正常但是进程会在运行时爆炸。

为了说明这一点

class Foo implements Serializable { .... // all good } class Foo implements Serializable { // OOps, executorService is not serializable. It's not declared as transient either private ExecutorService executorService = .. } 

我正在考虑编写一个unit testing,通过所有课程并确保“真正的可串行化”。 我已经阅读了一些关于连续特定对象的讨论。 我理解这个过程,但它需要

1)创建一个对象。
2)序列化然后
3)反序列化。

是否有更有效和实用的方法。 也许用reflection。 通过所有类,如果类具有可序列化,则所有属性必须是可序列化的或具有临时关键字。

想法?

1)创建一个对象。 2)序列化然后3)反序列化。

此列表不完整; 你还需要初始化。 考虑这个例子:

 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 } 

你可以序列化和反序列化第一个,但是你会在第二个上得到NotSerializableException 。 为了使问题进一步复杂化,如果使用接口,你永远无法判断一个类是否会传递序列化,因为它是流接口的这个接口后面的的具体对象:

 class PerhapsCanBeSerializedButYouNeverKnow implements Serializable { private Runnable r; // interface type - who knows? } 

前提是您可以保证您的类所使用的所有类和类的以下内容都要进行测试:

  • 默认构造函数存在,
  • 字段中没有接口类型,

然后你可以通过reflection自动创建和初始化它们,然后测试序列化。 但这是一个非常艰难的条件,不是吗? 否则,正确的初始化归结为手动工作。

您可以以不同的方式使用reflection:遍历要检查的Class对象列表,获取它们的Field[] ,并validation它们是否是瞬态的( Field.getModifiers() )或者它们是否直接实现SerializableField.getType().getInterfaces() )或间接(通过超级接口或类)。 另外,根据序列化机制的工作深度,考虑要检查的深度。

正如Ryan正确指出的那样,如果代码足够邪恶,这种静态序列化检查会失败:

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

或者只是如果readObject()/writeObject()被严重实现。 要测试这种问题,您需要实际测试序列化过程,而不是它背后的代码。

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

 @Test public void aFooSerializesAndDeserializesCorrectly { Foo fooBeforeSerialization = new Foo(); ReflectionUtils.randomlyPopulateFields(foo); Foo fooAfterSerialization = Serializer.serializeAndDeserialize(foo); assertThat(fooAfterSerialization, hasSameFieldValues(fooBeforeSerialization)); } 

编辑: randomPopulateFields的一个简单实现:

 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; } } }); }