使用辅助方法为 Java 个对象生成测试数据时提供默认值

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

假设以下 POJO:

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

现在我想为此class生成各种测试数据。每当某些字段未明确设置时,应设置默认值。请记住,我不能在原来的class上加上Lombok的@Builder@Builder.Default,也不能用其他方式修改

所以我想出了下面的帮手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;
    }
}

虽然这对于只有一些字段的 classes 是可以的,但是如果有更多的字段并且对于测试数据的某些变化,它会变得乏味。

我的第一个想法是在 createUserRequest() 的方法级别上使用 Lombok 的 @Builder:

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

这将允许以下内容:

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

但是,这不会为(在本例中 email)设置任何默认值。是否有一些模式可以简化这种方法?

为什么不...

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

如果有人运行 new UserRequest(),那将起作用,并且该 UR 将具有默认值。 .set 可用于更改任何或所有这些字段的值。

免责声明:我是 lombok 的核心贡献者之一。

-- 编辑--

啊哈,你不能改变原来的。如果允许 null 算作哨兵(例如,作为需要默认值的信号):

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

如果您希望 UR 设置某些默认值,但如果有人明确地 buildUserRequest().username(null).build() 那么,username 应该是 null,oof。如果您不能更改 UserRequest 的来源,我不确定是否有更简单的方法。

您实际上可以通过在您的 util class 中使用一些变通方法来实现此目的,以及使用 @Builder 注释的方法添加以下 class 与构建器同名 class 将由 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 仍会像往常一样在构建器中生成其他所需的方法 class。您可以像这样覆盖 lombok 生成的构建器 class 中任何 methods/fields 的行为。 (不确定这样做是否正确,但确实有效!)

注意: 在构建对象时传递显式 null 会将值设置为 null

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