Provide default values when generating test data for Java objects with helper methods

Viewed 107

Assume the following POJO:

@Getter
@Setter
public class UserRequest {
    private String username;
    private String password;
    private String email;
}

Now I'd like to generate various test data for this class. Whenever some field is not set explicitly, a default value shall be set. Please bear in mind that I cannot add Lombok's @Builder or @Builder.Default to the original class or modify it in other ways.

So I came up with the following helper class:

public class UserRequestTestDataUtils {

    public static final String DEFAULT_USERNAME = "foo";
    public static final String DEFAULT_PASSWORD = "bar";
    public static final String DEFAULT_EMAIL = "foo@bar.com";

    public static UserRequest createUserRequestWithUsername(String username) {
        return createUserRequest(username, DEFAULT_PASSWORD, DEFAULT_EMAIL);
    }

    public static UserRequest createUserRequestWithPassword(String password) {
        return createUserRequest(DEFAULT_USERNAME, password, DEFAULT_EMAIL);
    }

    public static UserRequest createUserRequestWithEmail(String email) {
        return createUserRequest(DEFAULT_USERNAME, DEFAULT_PASSWORD, email);
    }

    public static UserRequest createUserRequestWithUsernameAndPassword(String username, String password) {
        return createUserRequest(username, password, DEFAULT_EMAIL);
    }

    public static UserRequest createUserRequest(String username, String password, String email) {
        final UserRequest userRequest = new UserRequest();
        userRequest.setUsername(username);
        userRequest.setPassword(password);
        userRequest.setEmail(email);    
        return userRequest;
    }
}

While this is ok for classes with only some fields, it gets tedious if there are more fields and for certain variations of the test data.

My first idea was to use Lombok's @Builder on the method level for createUserRequest():

@Builder
public static UserRequest createUserRequest(String username, String password, String email) {
    final UserRequest userRequest = new UserRequest();
    userRequest.setUsername(username);
    userRequest.setPassword(password);
    userRequest.setEmail(email);    
    return userRequest;
}

Which would allow the following:

UserRequestTestDataBuilder.builder()
  .username("foo")
  .password("bar")
  .build();

However, this doesn't set any default value for (in this case email). Is there some pattern which could simplify this approach?

2 Answers

Why not just...

@Getter
@Setter
public class UserRequest {
    private String username = "";
    private String password = "";
    private String email = "(unknown email)";
}

If someone runs new UserRequest(), that will work, and that UR will have the default values. .set can be used to change the value of any or all of these fields.

DISCLAIMER: I'm one of the core contributors of lombok.

-- EDIT --

Aha, you can't change the original. If null is allowed to count as a sentinel (as in, as a signal that the default is required):

@Builder
public static UserRequest newUserRequest(String username, String password, String email) {
    UserRequest ur = new UserRequest();
    ur.setUsername(username == null ? DEFAULT_USERNAME : username);
    // etc
    return ur;
}

If instead you want the UR to get set up with certain defaults, but if someone explicitly does buildUserRequest().username(null).build() then, username should be null, oof. I'm not sure there is an easier way if you can't change the source of UserRequest.

You could actually achieve this with a little workaround in your util class, along with the method annotated with @Builder add the below class with the same name as the builder class that would be generated by lombok.

public class UserRequestTestDataUtils {
    // leave this method as such, without any null checks.
    @Builder
    public static UserRequest newUserRequest(String username, String password, String email) {
        UserRequest ur = new UserRequest();
        ur.setUsername(username);
        // etc
        return ur;
    }

    // Provide the default values here.
    public static class UserRequestBuilder {
        private String username = "foo";
        private String password = "bar";
        private String email = "foo@bar.com";
    } 
}    

Lombok would still generate the other required methods in the builder class as usual. You could override the behavior of any methods/fields within the lombok generated builder class like this. (Not sure if this is a right thing to do, but it works!)

NOTE : Passing an explicit null while building the object would set the value as null

//this would generate password and email with the given default values.
UserRequestTestDataUtils.builder().username("testname").build();
Related