我需要做的是进行单元测试,检查给定的类MyClass是否可序列化。但我不仅可以检查该类是否实现了Serializable:类的所有属性(以及属性的属性等)都必须实现Serializable。
此外,创建MyClass实例并尝试对其进行序列化和反序列化的解决方案并不令人满意:如果有人添加了属性但未更新单元测试,则单元测试将不会测试新属性。
一种解决方案可能是递归地使用反射来测试所有属性的类是否实现Serializable,但这看起来非常重。
有没有更好的解决方案?
感谢。
答案 0 :(得分:3)
这是一个老问题,但我想我会添加我的2c。
即使您遍历整个对象,也无法保证对象实现可序列化。如果您有一个抽象的成员变量或者是一个接口,则无法说明您最终存储的对象是否将被序列化。如果你知道自己在做什么,它会被序列化。
This answer provides a solution by populating your objects with random data,但这可能是矫枉过正的。
我创建了一个类,它通过忽略接口,java.lang.Object和抽象类的结构来进行递归。
package au.com.tt.util.test;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class SerializableChecker
{
public static class SerializationFailure
{
private final String mContainingClass;
private final String mMemberName;
public SerializationFailure(String inNonSerializableClass, String inMemberName)
{
mContainingClass = inNonSerializableClass;
mMemberName = inMemberName;
}
public String getContainingClass()
{
return mContainingClass;
}
public String getMemberName()
{
return mMemberName;
}
public String getBadMemberString()
{
if (mMemberName == null)
return mContainingClass;
return mContainingClass + "." + mMemberName;
}
@Override
public String toString()
{
return "SerializationFailure [mNonSerializableClass=" + mContainingClass + ", mMemberName=" + mMemberName + "]";
}
}
private static class SerializationCheckerData
{
private Set<Class<?>> mSerializableClasses;
SerializationCheckerData()
{
mSerializableClasses = new HashSet<Class<?>>();
}
boolean isAlreadyChecked(Class<?> inClass)
{
return mSerializableClasses.contains(inClass);
}
void addSerializableClass(Class<?> inClass)
{
mSerializableClasses.add(inClass);
}
}
private SerializableChecker()
{ }
public static SerializationFailure isFullySerializable(Class<?> inClass)
{
if (!isSerializable(inClass))
return new SerializationFailure(inClass.getName(), null);
return isFullySerializable(inClass, new SerializationCheckerData());
}
private static SerializationFailure isFullySerializable(Class<?> inClass, SerializationCheckerData inSerializationCheckerData)
{
for (Field field : declaredFields(inClass))
{
Class<?> fieldDeclaringClass = field.getType();
if (field.getType() == Object.class)
continue;
if (Modifier.isStatic(field.getModifiers()))
continue;
if (field.isSynthetic())
continue;
if (fieldDeclaringClass.isInterface() || fieldDeclaringClass.isPrimitive())
continue;
if (Modifier.isAbstract(field.getType().getModifiers()))
continue;
if (inSerializationCheckerData.isAlreadyChecked(fieldDeclaringClass))
continue;
if (isSerializable(fieldDeclaringClass))
{
inSerializationCheckerData.addSerializableClass(inClass);
SerializationFailure failure = isFullySerializable(field.getType(), inSerializationCheckerData);
if (failure != null)
return failure;
else
continue;
}
if (Modifier.isTransient(field.getModifiers()))
continue;
return new SerializationFailure(field.getDeclaringClass().getName(), field.getName());
}
return null;
}
private static boolean isSerializable(Class<?> inClass)
{
Set<Class<?>> interfaces = getInterfaces(inClass);
if (interfaces == null)
return false;
boolean isSerializable = interfaces.contains(Serializable.class);
if (isSerializable)
return true;
for (Class<?> classInterface : interfaces)
{
if (isSerializable(classInterface))
return true;
}
if (inClass.getSuperclass() != null && isSerializable(inClass.getSuperclass()))
return true;
return false;
}
private static Set<Class<?>> getInterfaces(Class<?> inFieldDeclaringClass)
{
return new HashSet<Class<?>>(Arrays.asList(inFieldDeclaringClass.getInterfaces()));
}
private static List<Field> declaredFields(Class<?> inClass)
{
List<Field> fields = new ArrayList<Field>(Arrays.asList(inClass.getDeclaredFields()));
Class<?> parentClasses = inClass.getSuperclass();
if (parentClasses == null)
return fields;
fields.addAll(declaredFields(parentClasses));
return fields;
}
}
答案 1 :(得分:1)
作为一般性检查,您可以使用Apache Commons中的SerializationUtils
,如下所示:
byte [] data = SerializationUtils.serialize(obj);
Object objNew = SerializationUtils.deserialize(data);
但它不保证该类的其他实例将正确序列化;对于复杂对象,存在特定成员无法序列化的风险,具体取决于他们的成员。
答案 2 :(得分:0)
我认为序列化和反序列化实例是可以的。如果更改了类,则单元测试仍然有效,您需要比较原始对象和反序列化对象
答案 3 :(得分:0)
递归地使用反射并不是完美的方法,但是可以部分地完成工作。
为此,您可以重用this之类的实用工具类。
用法如下:
public class SerializationTests {
@Test
public void ensure_MyClass_is_serializable() {
assertIsSerializable(MyClass.class);
}
@Test
public void ensure_MyComplexClass_is_serializable() {
// We excludes "org.MyComplexClass.attributeName"
// because we can not be sure it is serializable in a
// reliable way.
assertIsSerializable(MyComplexClass.class, "org.MyComplexClass.attributeName");
}
private static void assertIsSerializable(Class<?> clazz, String... excludes) {
Map<Object, String> results = SerializationUtil.isSerializable(clazz, excludes);
if (!results.isEmpty()) {
StringBuilder issues = new StringBuilder();
for (String issue : results.values()) {
issues.append("\n");
issues.append(issue);
}
fail(issues.toString());
}
}
}
答案 4 :(得分:0)
一种简单的方法来测试对象是否可以在Kotlin中进行序列化:
/**
* Check if an object is Serializable
*/
fun assertIsSerializable(obj: Any) {
val serialized = SerializationUtils.serialize(obj as Serializable)
val unSerialized = SerializationUtils.deserialize<Any>(serialized)
}