用Java测试builder模式
我们在代码库中广泛使用构建器模式,构建的对象都有一个用Java测试builder模式,java,unit-testing,builder,Java,Unit Testing,Builder,我们在代码库中广泛使用构建器模式,构建的对象都有一个toBuilder()方法。我想编写一个单元测试,确保在toBuilder()方法中没有忘记任何字段,也就是说,对于任何可构建对象,我想进行大致如下的测试 MyClass obj = getTestObjectWithRandomData(); assertEquals(obj, obj.toBuilder().build()); 现在,我可以相当轻松地编写一个基本版本的getestobjectwithrandomdata(),它使用反射为任
toBuilder()
方法。我想编写一个单元测试,确保在toBuilder()
方法中没有忘记任何字段,也就是说,对于任何可构建对象,我想进行大致如下的测试
MyClass obj = getTestObjectWithRandomData();
assertEquals(obj, obj.toBuilder().build());
现在,我可以相当轻松地编写一个基本版本的getestobjectwithrandomdata()
,它使用反射为任何对象的字段分配一组值。然而,问题是build()
通常包含大量的验证检查,例如,如果某个整数不在正常范围内,这些检查将引发异常。编写符合所有这些特定于类的验证检查的通用版本的GetTestObject WithRandomData()
是不可能的
那么,我怎样才能做我想做的事呢?我试图将构造代码和验证代码分离到不同的方法中,这样测试就不会在验证时出错,但这意味着人们必须记住在创建对象后调用validate()
或其他方法。不太好
还有其他想法吗?使用龙目山怎么样?这是你的选择吗?它将自动生成生成器代码,您再也不用担心它了。 只需使用
@Builder
与龙目
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
private String name;
private int age;
@Singular private Set<String> occupations;
}
导入lombok.Builder;
进口龙目山。单数;
导入java.util.Set;
@建筑商
公共类生成器示例{
私有字符串名称;
私人互联网;
@单一的私人职业;
}
香草爪哇
import java.util.Set;
public class BuilderExample {
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
// go to https://projectlombok.org/features/Singular-snippet.html to see it.
Set<String> occupations = ...;
return new BuilderExample(name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
import java.util.Set;
公共类生成器示例{
私有字符串名称;
私人互联网;
私人职业;
BuilderExample(字符串名称、整数年龄、设置职业){
this.name=名称;
这个。年龄=年龄;
这个职业=职业;
}
公共静态构建器ExampleBuilder(){
返回新的BuilderExampleBuilder();
}
公共静态类生成器ExampleBuilder{
私有字符串名称;
私人互联网;
私有java.util.ArrayList职业;
BuilderExampleBuilder(){
}
公共构建器示例构建器名称(字符串名称){
this.name=名称;
归还这个;
}
公共建筑示例建筑商年龄(整数年龄){
这个。年龄=年龄;
归还这个;
}
公共构建器示例构建器占用(字符串占用){
如果(this.occulations==null){
this.occulations=new java.util.ArrayList();
}
本.职业.添加(职业);
归还这个;
}
public builder示例builder职业(集合为什么无法在getTestWithRandomData()中随机分配可接受的值)?在编写代码时,请询问所有的if语句和抛出,以及基本上所有的分支和圈竞争性,因为您现在必须去测试它们中的每一个。过去,我在类中使用了一个静态方法:MyClass.createTestObject()
。它在没有构建器的情况下构建了一个有效的对象,但所有字段都是有效的。随机性不是必需的,覆盖率是必需的。这对我来说是一种不必要的感觉。如果你知道构建器曾经正常工作过一次,并且你已经使用反射或其他一般方法构建了它,为什么你会停止相信它?看起来你坚持要n每次使用都要测试它,因为你想看到100%的代码覆盖率。这是一种你应该战胜的疾病。这是一个好主意。我以前见过这个库。不过,我的犹豫有两个方面。第一,我不确定我的同事和我是否准备好全力以赴。第二,使用Lombok意味着我必须放弃ctor中的那些验证检查。如果没有这些,就没有问题了。我已经晚了很多年,但是你也可以覆盖Lombok为你生成的内容。我偶尔这样做是为了提供一些验证检查。OTOH,你越是覆盖Lombok,代码就变得越惊人,所以它是一个混合包。