创建一个只用于测试的setter方法是否合适?

huangapple go评论81阅读模式
英文:

is making a setter method for only test fine?

问题

以下是您请求的代码部分的中文翻译:

我正在使用Mockito和Junit5进行单元测试

我根据用户的createdAt字段和今天的日期获取之前的学习记录所以我想检查我的代码是否获取了学习记录但问题是createdAt是由JPAAuditing设置的

@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(unique = true, nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;

    @Enumerated(EnumType.STRING)
    private UserRole role;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    protected User() {}

    protected User(String username, String email, String password) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.role = UserRole.USER;
    }

    public static User of(String username, email, String password, boolean verified) {
        return new User(username, email, password, verified);
    }

    public void setIdForTest(Long id) {
        this.id = id;
    }

    public void setPasswordForTest(String pw) {
        this.password = pw;
    }

    public void setCreatedAtForTest() {
        this.createdAt = LocalDateTime.now();
    }

    public void setCreatedAtForTest(LocalDateTime localDateTime) {
        this.createdAt = localDateTime;
    }
}

这是获取学习记录的代码

@Override
public UserDTO.UserDetailWithStudyRecords findPriorStudyRecords(User user) {
    LocalDateTime joinDate = user.getCreatedAt();
    LocalDateTime date = LocalDateTime.now();
    List<String> studiedDates = wordQuerydslRepository.findByUserAndCreatedAtBetweenAndGroupBy(
        user,
        LocalDateTime.of(joinDate.getYear(), joinDate.getMonth(), 1, 0, 0, 0),
        LocalDateTime.of(date.getYear(), date.getMonth(), date.toLocalDate().lengthOfMonth(), 23, 59, 59)
    );
    // ...
}

我需要操作joinDate但我想知道是否可以创建一个名为"setCreatedAtForTest"的测试方法

[测试代码]

@DisplayName("获取学习记录")
@Test
void getStudyRecords() {
    User user = UserFixture.getVerifiedUser();
    user.setCreatedAtForTest(LocalDateTime.of(2023, 01, 01, 00, 00));
    LocalDateTime now = LocalDateTime.now();
    List<String> dates = List.of("01/01/2023", "01/02/2023", "02/02/2023", "03/01/2023", "04/01/2023");
    given(wordQuerydslRepository.findByUserAndCreatedAtBetweenAndGroupBy(
        user,
        user.getCreatedAt(),
        LocalDateTime.of(now.getYear(), now.getMonth(), now.toLocalDate().lengthOfMonth(), 23, 59, 59))
    ).willReturn(dates);
}

请注意,代码中的HTML实体已被翻译为相应的字符。

英文:

I'm doing unit test using Mockito and Junit5.

I'm bringing prior study records based on user createdAt field and today date.
So I want to check if my code bring study records but the thing is createdAt is seted up by JPAAuditing.

@Getter
@ToString(callSuper = true)
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String password;
@Enumerated(EnumType.STRING)
private UserRole role;
@JsonFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
protected User() {}
protected User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
this.role = UserRole.USER;
}
public static User of(String username, String email, String password, boolean verified) {
return new User(username, email, password, verified);
}
public void setIdForTest(Long id) {
this.id = id;
}
public void setPasswordForTest(String pw) {
this.password = pw;
}
public void setCreatedAtForTest() {
this.createdAt = LocalDateTime.now();
}
public void setCreatedAtForTest(LocalDateTime localDateTime) {
this.createdAt = localDateTime;
}

}

This is the code that bringing study records

@Override
public UserDTO.UserDetailWithStudyRecords findPriorStudyRecords(User user) {
LocalDateTime joinDate = user.getCreatedAt();
LocalDateTime date = LocalDateTime.now();
List&lt;String&gt; studiedDates = wordQuerydslRepository.findByUserAndCreatedAtBetweenAndGroupBy(
user,
LocalDateTime.of(joinDate.getYear(), joinDate.getMonth(), 1, 0, 0, 0),
LocalDateTime.of(date.getYear(), date.getMonth(), date.toLocalDate().lengthOfMonth(), 23, 59, 59)
);
...
}

I need to manipulate the joinDate. But im curious if making a test method like "setCreatedAtForTest".

[Test Code]

@DisplayName(&quot;공부한 날짜 기록을 가져온다.&quot;)
@Test
void getStudyRecords() {
User user = UserFixture.getVerifiedUser();
user.setCreatedAtForTest(LocalDateTime.of(2023, 01, 01, 00, 00));
LocalDateTime now = LocalDateTime.now();
List&lt;String&gt; dates = List.of(&quot;01/01/2023&quot;, &quot;01/02/2023&quot;, &quot;02/02/2023&quot;, &quot;03/01/2023&quot;, &quot;04/01/2023&quot;);
given(wordQuerydslRepository.findByUserAndCreatedAtBetweenAndGroupBy(
user,
user.getCreatedAt(),
LocalDateTime.of(now.getYear(), now.getMonth(), now.toLocalDate().lengthOfMonth(), 23, 59, 59))
).willReturn(dates);
}

答案1

得分: 0

首先,作为一种替代方法,评论中@Carols的建议是正确的:您可以在test/resources目录中添加一个名为data.sql的文件,用于初始化数据库并插入一些测试数据。

如果您没有这个替代方法,我会建议创建一个包可见的setter用于测试。或者更好的做法是创建一个包可见的构造函数,该构造函数接收创建日期作为参数。

protected User(String username, String email, String password) {
    this(username, email, password);
}

User(String username, String email, String password, LocalDateTime createdDate) {
    this.username = username;
    this.email = email;
    this.password = password;
    this.role = UserRole.USER;
    this.createdDate = createdDate;
}

这将允许您从测试装置中注入日期来创建用户,或者从与User在同一包中定义的测试生成器中创建用户。即使Uncle Bob在他的一篇文章中指出“测试胜过封装”,但这当然是最后的手段措施:https://blog.cleancoder.com/uncle-bob/2015/07/01/TheLittleSingleton.html

英文:

You have a couple of options for solving this. Firstly, as an alternative, @Carols's suggestion in the comments is correct: you can use add a data.sql file in your test/resources directory that initializes the database with some test entries.

If you wouldn't have this alternative, I would say it's ok to create a package-protected setter for tests. Or even better, a packaged-protected constructor that receives the created date.

protected User(String username, String email, String password) {
this(username, email, password);
}
User(String username, String email, String password, LocalDateTime createdDate) {
this.username = username;
this.email = email;
this.password = password;
this.role = UserRole.USER;
this.createdDate = createdDate;
}

This will allow you to create a user injecting that date from the test fixture or from some test builder that is defined in the same package as the User. Even Uncle Bob has an article here he states that "testing trumps encapsulation" - but, of course, this a measure of last resort: https://blog.cleancoder.com/uncle-bob/2015/07/01/TheLittleSingleton.html

huangapple
  • 本文由 发表于 2023年4月10日 18:22:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75976250.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定