javaunit-testingjunitjunit5

Testing validation method of object that uses builder pattern only without any setters


I have domain aggregate Employee which uses builder pattern to set its fields during object construction. It has various lifecycle methods that mutate the state but no direct setters. It also has a validate() method which checks some conditions on the fields set by the builder and therefore can fail (throw exception) for multiple reasons.

The aggregate is somewhat like below:

public class Employee {

  private String field1;
  private String field2:

  //say 10 fields

  public static Builder builder() {
    return new Builder();
  }

  public static class Builder {
    // builder fields and methods
  }

  //public lifecycle methods

  public void validate() {
    if (field1 == null) {
       //throw custom exception
    }
    if (field2 == null) {
       //throw custom exception
    }
    // More condition checks on fields (not all are null checks)
  } 
}

Now my test class is organized as below:

public class EmployeeTest {
     
    @Test
    public void givenNewEmployee_IfField1IsMissing_thenThrowsException() {
      
      //Setup
      Employee testEmployee = Employee.builder()
                               .field2(nonNullValue)
                               //other fields
                               .build();

 
      Assertions.assertThrows(CustomException.class, testEmployee::validate);
    }

     @Test
    public void givenNewEmployee_IfField2IsMissing_thenThrowsException() {
      
      //Setup
      Employee testEmployee = Employee.builder()
                               .field1(nonNullValue)
                               //other fields
                               .build();

      //Test
      Assertions.assertThrows(CustomException.class, testEmployee::validate);
    }
 
}

In the above, for each test case I have to build the whole Employee test object testEmployee except one field in the build() method. This makes my test cases clumsy and take many lines of code.

My question is:

Is there a way I can use to setup the testEmployee object once and then set the individual fields to null in each of the test cases so that my tests are concise and doesn't require building the entire testEmployee?

I am aware of @BeforeAll and @BeforeEach annotations for setting up tests. But since my Employee class does not provide any setter I have no way to setup a one valid testEmployee in @BeforeEach and then set the individual fields to null in each of the test cases. So I need alternatives if available.


Solution

    1. In @BeforeEach create builder and fill all fields. Then store it.

    2. In each test set field to null, then build and validate.

    Example:

    class EmployeeTest {
        Employee.Builder builder;
    
        @BeforeEach
        void init(){
            builder = Employee.builder()
              .field1("some value")
              .field2("another value")
        }
        @Test
        void testField1Value() {
           Employee e = builder.field1(null).build();
           e.validate();
        }
    }
    

    Alternatively you can create toBuilder method. Then you can do employee.toBuilder().fieldq(null).build().validate().