在JUnit测试中使用Builder模式

这并不是要成为技术含量很高的职位。 这篇文章的目的是为您提供一些指导,以使您的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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值