【问题标题】:Provide default values when generating test data for Java objects with helper methods使用辅助方法为 Java 对象生成测试数据时提供默认值
【发布时间】:2021-06-18 13:37:21
【问题描述】:

假设以下 POJO:

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

现在我想为这个类生成各种测试数据。每当没有明确设置某些字段时,应设置默认值。请记住,我不能将 Lombok 的 @Builder@Builder.Default 添加到原始类或以其他方式修改它

所以我想出了以下帮助类:

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

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

我的第一个想法是在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)设置任何默认值。是否有一些模式可以简化这种方法?

【问题讨论】:

    标签: java lombok builder


    【解决方案1】:

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

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

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

    【讨论】:

    • @RobertStrauch 当您在newUserRequest 方法之上使用@Builder 时,lombok 会生成一个内部构建器类。我们通过创建一个类似的“同名”类来覆盖它。但诀窍是并非所有内容都被覆盖。 lombok 为构建器类生成的其余代码保持不变。
    • 太好了,我只是在我的字段名称中有错字。这似乎可以解决问题。但它需要重复原始类中的所有字段,对吧?
    • @RobertStrauch 否。不适用于所有字段。只有那些你需要默认值的。
    • 这似乎解决了我的问题。猜猜我也会尝试@rzwitserloot 描述的变体,然后决定哪个更适合:-)
    • @RobertStrauch 是的,使用这种方法,您无需编写此构建器类。我认为上述方法在这些情况下也可能会派上用场-> stackoverflow.com/a/67401875/7804477
    【解决方案2】:

    为什么不...

    @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 的来源,我不确定是否有更简单的方法。

    【讨论】:

    • 虽然我可以访问UserRequest 类,但我不能修改它。因此,直接在类中设置默认值不是一种选择。此外,默认值仅适用于测试对象。我应该在我的问题中更明确地说明这一点。
    • 是的,您的编辑准确地描述了它的含义,包括。 “当 null 被传递时,然后设置 null”,但我想为这些情况提供专用的帮助方法更容易。它们并不常见。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 1970-01-01
    • 2019-07-25
    相关资源
    最近更新 更多