如何编写单元测试来检查复制构造函数与类属性是否同步?

问题描述 投票:0回答:2

最近我们的系统中出现了一个错误,该错误是由于忘记在复制构造函数中分配新添加的类属性而引起的。

例如:

public class MyClass {

    private Long companyId;
    private Long classId;
    private Double value;   <=  newly added

    public MyClass(MyClass myClass) {
        this.setCompanyId(myClass.getCompanyId());
        this.setClassId(myClass.getClassId());
        this.setValue(myClass.getValue());   <= we forget this line
    }

我想编写一个单元测试,保证在添加新属性时捕获丢失的副本分配。我应该如何进行?

java unit-testing junit copy-constructor
2个回答
0
投票

我会通过反思来做到这一点。

  • 新建
    MyClass
    的实例。 (一)
  • 通过反射,为每个字段分配一个值。这可以通过
    getClass().getDeclaredFields()
    来完成。
  • 新建
    MyClass
    的空白实例。 (B)
  • 在 A 上针对 B 运行复制构造函数。
  • 通过反射判断A中的字段是否等于B。

0
投票

IMO,有三种方法可以解决这个问题

  1. 使用将自动更新的复制构造函数(例如使用 Lombok)
@Builder(toBuilder=true)
class Foo {
   int x;
   ...
}
Foo f0 = Foo.builder().build();
Foo f1 = f0.toBuilder().build();  // copy
  1. 创建一个测试,用非默认值填充对象,然后使用反射/杰克逊读取其所有键值对,并确保该值不是默认值(
    null
    0
    0.0
    false
    '\0'
import java.util.Objects;
// other imports

public class FooTest {
  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

  @Test
  public void testCopyConstructor_shouldCopyAllFields() {
    // should be instantiated with non-default values
    Foo original = new Foo();
    
    original.setSimilarity(0.1);
    // set your fields here

    Foo copied = new Foo(original);

    // You can use reflections if you don't want to add Jackson as your dependency
    Map<String, Object> originalMap = OBJECT_MAPPER.convertValue(original, new TypeReference<HashMap<String, Object>>(){});
    for (Object value : originalMap.values()) {
      // if we add new fields and forget to update this test, this test should fail
      assertTrue(value != null && !Objects.equals(value, 0.0)
        && !Objects.equals(value, 0) && !Objects.equals(value, false)
        && !Objects.equals(value, '\0'), "");
    }

    Map<String, Object> resultingMap = OBJECT_MAPPER.convertValue(copied, new TypeReference<HashMap<String, Object>>(){});
    for (Map.Entry<String, Object> entry : resultingMap.entrySet()) {
      Object expectedValue = originalMap.get(entry.getKey());
      assertEquals(entry.getValue(), expectedValue);
    }
  }
}
  1. 使用复杂的对象随机化器,例如EasyRandom,并在单元测试中使用它并执行复制构造函数
import java.util.Objects;
// other imports

public class FooTest {
  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

  @Test
  public void testCopyConstructor_shouldCopyAllFields() {
    EasyRandom generator = new EasyRandom();
    
    for (int i = 0; i < 4; i++){  // you can do it multiple times if you're unsure
      Person person = generator.nextObject(Person.class);
      Person copied = new Person(person);

      // or if you afraid of forgetting to update your .equals() method, 
      // just convert both of them to hashmaps (using jackson) 
      // and compare the hashmaps
      assertEquals(copied, person);  
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.