Using Builder Pattern in JUnit tests

This is not intended to be a heavily technical post. The goal of this post is to give you some guidelines to make your JUnit testing life more easy, to enable you to write complex scenarios for tests in minutes with the bonus of having extremely readable tests.
 
 
 
 
 
 
 
 
There are two major parts in a Unit tests that require writing a lot of bootstrap code:

  • the setup part: constructing your initial state requires building the initial objects that will be fed to your SUT (system under test)
  • the assertion part: constructing the desired image of your output objects and making assertions only on the needed data.

 
In order to reduce the complexity of building objects for tests I suggest using the Builder pattern in the following interpretation:

Here is the domain object:

public class Employee {
    private int id;
    private String name;
    private Department department;

    //setters, getters, hashCode, equals, toString methods

The builder for this domain object will look like this:

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;
    }
}

As you can see we have some factory methods:

public static EmployeeBuilder defaultValues()
    public static EmployeeBuilder clone(Employee toClone)
    public static EmployeeBuilder random()

These methods return different builders:

  • defaultValues : some hardcoded values for each fields ( or the Java defaults – current implementation)
  • clone : will take all the values from the initial object, and give you the possibility to change just some
  • random : will generate random values for each field. This is very useful when you have a lot of fields that you don’t specifically need in your test, but you need them to be initialized. getRandom* methods are defined statically in another class.

You can add other methods that will initialized your builder accordingly to your needs.

Also the builder can handle building some objects that are not so easily constructed and changed. For example let’s change a little bit the Employee object and make it immutable:

public class Employee {
    private final int id;
    private final String name;
    private final Department department;
    ...
}

Now we lost the possibility to change the fields as we wish. But using the builder in the following form we can regain this possibility when constructing the object:

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);
    }
}

This is very useful when we have hard to construct objects, or we need to change fields that are final.

An here its the final result:

Without builders:

@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));
    }

With builders:

@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));
    }

As you can see, the size of the test is much smaller, and the construction of objects became much simpler (and nicer if you have a better code format). The difference is greater if you have a more complex domain object (which is more likely in real-life applications and especially in legacy code).

Have fun!
 

Reference: Using Builder Pattern in JUnit tests from our JCG partner  Stefan Bulzan at the Java Advent Calendar blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

One Response to "Using Builder Pattern in JUnit tests"

  1. thens says:

    Additionally one could/should add the Builder Pattern and/with Fluent interface!

Leave a Reply


6 + = fourteen



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close