Java 如何通过单元测试exp serialVersionUID覆盖私有静态最终类成员
我正在添加单元测试以覆盖遗留java projet(我无法更改) 我知道如何进行单元测试并涵盖这个类,我是通过添加下面的单元测试来实现的Java 如何通过单元测试exp serialVersionUID覆盖私有静态最终类成员,java,junit,sonarqube,code-coverage,jacoco,Java,Junit,Sonarqube,Code Coverage,Jacoco,我正在添加单元测试以覆盖遗留java projet(我无法更改) 我知道如何进行单元测试并涵盖这个类,我是通过添加下面的单元测试来实现的 public class Thing implements Serializable { private static final long serialVersionUID = -1463850548670691860L; private String id; } 例如: class ThingTest { @Test
public class Thing implements Serializable {
private static final long serialVersionUID = -1463850548670691860L;
private String id;
}
例如:
class ThingTest {
@Test
void testShouldNotChangeSerialVersionUID() throws NoSuchFieldException {
final Field serialVersionUID = Thing.class.getDeclaredField("serialVersionUID");
serialVersionUID.setAccessible(true);
assertEquals(-1463850548670691860L, getField(serialVersionUID, new Thing()));
}
}
但是声纳和jacoco显示serialVersionUID未被覆盖。这是声音
请你提出一个解决方案,让声纳满意,覆盖这一领域
是的,我知道,我不应该遵循工具,而是实践
另外,我不能更改serialVersionUID的可见性
谢谢如果在JUnit中扩展ObjectInputString,则可以在反序列化序列化的对象时读取SerialVersionId:
private class MyObjectInputStream extends ObjectInputStream {
public long uid;
public MyObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
ObjectStreamClass readClassDescriptor = super.readClassDescriptor(); //To change body of generated methods, choose Tools | Templates.
uid = readClassDescriptor.getSerialVersionUID();
return readClassDescriptor;
}
}
@Test
public void testSomeMethod() throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(new Thing());
MyObjectInputStream myObjectInputStream = new MyObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
myObjectInputStream.readObject();
assertEquals(-1463850548670691860L, myObjectInputStream.uid);
}
您的测试覆盖率系统说“在单元测试期间,没有人访问此字段”。这就是红色条的意思。因此,为了让它消失,您需要运行访问该字段的代码。使用反射访问字段以增加单元测试覆盖率是毫无意义的。即使它可能会将字段变为绿色,但实际上它并没有以有意义的方式使用字段 正确的方法是以使用该字段的方式练习测试类。serialVersionUID在序列化/反序列化期间用于确定正在读取的字节是否与您尝试实例化的对象匹配 下面是一个简单的例子,涵盖了您想要的内容。我在末尾添加了一个相等性测试,因为这是当您关心序列化/反序列化过程时应该测试的内容
package org.does.not.exist;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import static org.assertj.core.api.Assertions.assertThat;
class Foo implements Serializable {
private static final long serialVersionUID = 42L;
public Foo(String value) {
this.value = value;
}
private String value;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Foo)) return false;
Foo foo = (Foo) o;
return value != null ? value.equals(foo.value) : foo.value == null;
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
}
class StackoverflowTest {
@Test
void test() throws Exception {
Foo foo = new Foo("a string!");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(foo);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bis);
Foo copied = (Foo) in.readObject();
assertThat(foo).isEqualTo(copied);
}
}
这样做,但是用您自己的类替换Foo,您应该得到覆盖率。抱歉,这只是一个好奇:覆盖serialVersionId有什么用?为该字段添加测试的实用程序是强制想要修改值的人也更改测试,因此,请阅读一条消息,向他解释为什么在这样做之前必须三思而后行,例如,确保
serialVersionUID
未被修改不会阻止用户以破坏序列化的方式修改类。如果您真的关心这一点,我建议您将对象的序列化形式保存到资源文件中,将其添加到src/test/resources
目录中,并编写一个测试,以确保资源文件可以正确反序列化。我同意您的观点,通过反射可以做到这一点,我还为您提到的特定案例进行了单元测试。您能用get方法包装它吗?这不会向后测试compatibility@Renato没错,不是。这只是证明您可以使用Java语言本机序列化过程写入和读取对象的最低有用测试,这将使您获得本文要求的100%测试覆盖率,同时实际上也是一个有意义的测试。IMHO不是正确答案,如果未指定SerialVersionUID,这也将起作用