gpt4 book ai didi

java - 如何编写单元测试以确保类定义未被修改?

转载 作者:行者123 更新时间:2023-11-30 03:45:07 24 4
gpt4 key购买 nike

我们有一个使用 java 序列化来序列化的类。这个类应该很少被修改。我想编写一个强制执行此规则的单元测试。当这个字段被修改时,这意味着很多变化,并且版本会跳转到依赖但独立的应用程序,这些应用程序使用该对象的 java 序列化来调用我们的应用程序。

一种想法是只使用自动生成的serialVersionUID。这样做的问题是它可以根据 JVM 的变化进行修改(这会很烦人)。

关于如何解决这个问题还有其他想法吗?

值得庆幸的是,类(class)非常平坦。或者,换句话说,该类没有复杂的子类。它只是简单的 java 对象的集合,如字符串、整数和数组。

最佳答案

最简单的选择是使用反射 API 获取类接口(interface)、非静态和非 transient 字段、构造函数和方法的列表,并断言它们没有更改。

这当然意味着您必须对受此序列化影响的每个类执行此操作(例如,如果您的根类具有其他对象的集合)。

作为引用,SVUID算法可以在Java serialization spec中找到:

The serialVersionUID is computed using the signature of a stream of bytes that reflect the class definition. The National Institute of Standards and Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a signature for the stream. The first two 32-bit quantities are used to form a 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data types to a sequence of bytes. The values input to the stream are defined by the Java Virtual Machine (VM) specification for classes. The sequence of items in the stream is as follows:

  1. The class name written using UTF encoding.
  2. The class modifiers written as a 32-bit integer.
  3. The name of each interface sorted by name written using UTF encoding.
  4. For each field of the class sorted by field name (except private static and private transient fields):
    1. The name of the field in UTF encoding.
    2. The modifiers of the field written as a 32-bit integer.
    3. The descriptor of the field in UTF encoding
  5. If a class initializer exists, write out the following:
    1. The name of the method, <clinit>, in UTF encoding.
    2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer.
    3. The descriptor of the method, ()V, in UTF encoding.
  6. For each non-private constructor sorted by method name and signature:
    1. The name of the method, <init>, in UTF encoding.
    2. The modifiers of the method written as a 32-bit integer.
    3. The descriptor of the method in UTF encoding.
  7. For each non-private method sorted by method name and signature:
    1. The name of the method in UTF encoding.
    2. The modifiers of the method written as a 32-bit integer.
    3. The descriptor of the method in UTF encoding.
  8. The SHA-1 algorithm is executed on the stream of bytes produced by DataOutputStream and produces five 32-bit values sha[0..4].
  9. The hash value is assembled from the first and second 32-bit values of the SHA-1 message digest. If the result of the message digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named sha, the hash value would be computed as follows:

    long hash = ((sha[0] >>> 24) & 0xFF)
    | ((sha[0] >>> 16) & 0xFF) << 8
    | ((sha[0] >>> 8) & 0xFF) << 16
    | ((sha[0] >>> 0) & 0xFF) << 24
    | ((sha[1] >>> 24) & 0xFF) << 32
    | ((sha[1] >>> 16) & 0xFF) << 40
    | ((sha[1] >>> 8) & 0xFF) << 48
    | ((sha[1] >>> 0) & 0xFF) << 56;

这里是使用 SerialVersionUIDAdder 重新计算 SVUID 的代码来自ASM 5.0框架:

    SerialVersionUIDAdder svuidv = new SerialVersionUIDAdder(Opcodes.ASM5, null) {
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if ("serialVersionUID".equals(name)) {
return null;
}
return super.visitField(access, name, desc, signature, value);
}

protected void addSVUID(long svuid) {
if(svuid!=expectedsvid) {
throw new AssertionError("Serialization issue!");
}
}
};

InputStream is = AA.class.getResourceAsStream("/" + AA.class.getName().replace('.', '/') + ".class");
ClassReader cr = new ClassReader(is);
cr.accept(svuidv, ClassReader.SKIP_CODE);

关于java - 如何编写单元测试以确保类定义未被修改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25938772/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com