这并不是要成为技术含量很高的职位。 这篇文章的目的是为您提供一些指导,以使您的JUnit测试生活更加轻松,使您能够在几分钟内编写复杂的测试场景,并具有易于阅读的测试优势。
单元测试中有两个主要部分,需要编写许多引导程序代码:
- 设置部分:构建初始状态需要构建将被馈送到SUT(被测系统)的初始对象
- 断言部分:构造输出对象的所需图像,并仅对所需数据进行断言。
为了降低构建用于测试的对象的复杂性,我建议在以下解释中使用Builder模式:
这是域对象:
public class Employee {
private int id;
private String name;
private Department department;
//setters, getters, hashCode, equals, toString methods
此域对象的生成器如下所示:
public class EmployeeBuilder {
private Employee employee;
public EmployeeBuilder() {
employee = new Employee();
}
public static EmployeeBuilder defaultValues() {
return new EmployeeBuilder();
}
public static EmployeeBuilder clone(Employee toClone) {
EmployeeBuilder builder = defaultValues();
builder.setId(toClone.getId());
builder.setName(toClone.getName());
builder.setDepartment(toClone.getDepartment());
return builder;
}
public static EmployeeBuilder random() {
EmployeeBuilder builder = defaultValues();
builder.setId(getRandomInteger(0, 1000));
builder.setName(getRandomString(20));
builder.setDepartment(Department.values()[getRandomInteger(0, Department.values().length - 1)]);
return builder;
}
public EmployeeBuilder setId(int id) {
employee.setId(id);
return this;
}
public EmployeeBuilder setName(String name) {
employee.setName(name);
return this;
}
public EmployeeBuilder setDepartment(Department dept) {
employee.setDepartment(dept);
return this;
}
public Employee build() {
return employee;
}
}
如您所见,我们有一些工厂方法:
public static EmployeeBuilder defaultValues()
public static EmployeeBuilder clone(Employee toClone)
public static EmployeeBuilder random()
这些方法返回不同的构建器:
- defaultValues:每个字段的一些硬编码值(或Java默认值-当前实现)
- clone:将获取初始对象中的所有值,并使您可以更改其中一些值
- random:将为每个字段生成随机值。 当您有很多字段在测试中不需要时非常有用,但是您需要将它们初始化。 getRandom *方法是在另一个类中静态定义的。
您可以添加其他方法来根据需要初始化构建器。
此外,构建器还可以处理一些不那么容易构建和更改的对象。 例如,让我们稍微更改Employee对象,使其不可变:
public class Employee {
private final int id;
private final String name;
private final Department department;
...
}
现在,我们失去了按需更改字段的可能性。 但是使用以下形式的构建器,我们可以在构造对象时重新获得这种可能性:
public class ImmutableEmployeeBuilder {
private int id;
private String name;
private Department department;
public ImmutableEmployeeBuilder() {
}
public static ImmutableEmployeeBuilder defaultValues() {
return new ImmutableEmployeeBuilder();
}
public static ImmutableEmployeeBuilder clone(Employee toClone) {
ImmutableEmployeeBuilder builder = defaultValues();
builder.setId(toClone.getId());
builder.setName(toClone.getName());
builder.setDepartment(toClone.getDepartment());
return builder;
}
public static ImmutableEmployeeBuilder random() {
ImmutableEmployeeBuilder builder = defaultValues();
builder.setId(getRandomInteger(0, 1000));
builder.setName(getRandomString(20));
builder.setDepartment(Department.values()[getRandomInteger(0, Department.values().length - 1)]);
return builder;
}
public ImmutableEmployeeBuilder setId(int id) {
this.id = id;
return this;
}
public ImmutableEmployeeBuilder setName(String name) {
this.name = name;
return this;
}
public ImmutableEmployeeBuilder setDepartment(Department dept) {
this.department = dept;
return this;
}
public ImmutableEmployee build() {
return new ImmutableEmployee(id, name, department);
}
}
当我们难以构造对象或需要更改最终字段时,这非常有用。
这是它的最终结果:
没有建设者:
@Test
public void changeRoleTestWithoutBuilders() {
// building the initial state
Employee employee = new Employee();
employee.setId(1);
employee.setDepartment(Department.DEVELOPEMENT);
employee.setName("John Johnny");
// testing the SUT
EmployeeManager employeeManager = new EmployeeManager();
employeeManager.changeRole(employee, Department.MANAGEMENT);
// building the expectations
Employee expectedEmployee = new Employee();
expectedEmployee.setId(employee.getId());
expectedEmployee.setDepartment(Department.MANAGEMENT);
expectedEmployee.setName(employee.getName());
// assertions
assertThat(employee, is(expectedEmployee));
}
与建设者:
@Test
public void changeRoleTestWithBuilders() {
// building the initial state
Employee employee = EmployeeBuilder.defaultValues().setId(1).setName("John Johnny").setDepartment(Department.DEVELOPEMENT).build();
// building the expectations
Employee expectedEmployee = EmployeeBuilder.clone(employee).setDepartment(Department.MANAGEMENT).build();
// testing the SUT
EmployeeManager employeeManager = new EmployeeManager();
employeeManager.changeRole(employee, Department.MANAGEMENT);
// assertions
assertThat(employee, is(expectedEmployee));
}
如您所见,测试的大小要小得多,对象的构造也变得更加简单(如果代码格式更好,也会更好)。 如果您具有更复杂的域对象(在实际应用程序中,尤其是在遗留代码中),则差异更大。
玩得开心!
参考:来自Java出现日历博客的JCG合作伙伴 Stefan Bulzan 在JUnit测试中使用了Builder模式 。
翻译自: https://www.javacodegeeks.com/2012/12/using-builder-pattern-in-junit-tests.html