작성
·
173
·
수정됨
0
공부삼아서 과제 프로젝트에 테스트를 도입하다가 궁금한 게 잇어서 질문드립니다.
거래내역 조회를 구현할 때 2024-05-30 같은 거래가 발생할 날짜는 auditing, @CreatedAt 통해 저장된 createdAt에서 가져오도록 구현했습니다. (디자인과 달리, 최근 거래일수록 위로 오도록 변경되었습니다) 구현이 끝난 뒤, 테스트할 때 createAt을 임의로 변경하기 위해 BaseEntity의 private를 protected, 그리고 createdAt을 변경하는 메서드를 추가햇습니다. (이게 맞는 접근인지는 잘 모르겟네요... 할 때에도 이렇게 해도 되나? 라는 의문이 계속 들엇습니다...)
@DisplayName("유저의 계좌 내역을 불러올 수 있다.")
@Test
void getUserStatements() {
// given (6/5 18:00)
LocalDateTime now = LocalDateTime.of(2024, 6, 5, 18, 0, 0);
User u1 = User.of("test1", "id1", "pw");
User u2 = User.of("test2", "id2", "pw");
List<User> users = userRepository.saveAll(List.of(u1, u2));
User loginUser = users.get(0);
User notLoginUser = users.get(1);
Account a1 = Account.of("111-222-3333", 1000, loginUser);
Account a2 = Account.of("111-222-3334", 2000, notLoginUser);
List<Account> accounts = accountRepository.saveAll(List.of(a1, a2));
Account loginUserAccount = accounts.get(0);
Account notLoginUserAccount = accounts.get(1);
// 자신 계좌로 입금 (조회 대상), 6/5 발생
Statement s1 = createStatementForTest(loginUserAccount, loginUserAccount, 100, DEPOSIT);
// 자신 계좌에서 출금 (조회 대상), 6/6 발생
Statement s2 = createStatementForTest(loginUserAccount, loginUserAccount, 200, WITHDRAW);
// 자신 계좌에서 타 계좌로 이체 (조회 대상), 6/7 발생
Statement s3 = createStatementForTest(loginUserAccount, notLoginUserAccount, 300, TRANSFER);
// 타 계좌에서 자신 계좌로 이체 (조회 대상), 6/8 발생
Statement s4 = createStatementForTest(notLoginUserAccount, loginUserAccount, 400, TRANSFER);
// 타 계좌에서 타 계좌로 출금 (조회 대상 X), 6/9 발생
Statement s5 = createStatementForTest(notLoginUserAccount, notLoginUserAccount, 50000, WITHDRAW);
statementRepository.saveAll(List.of(s1, s2, s3, s4, s5));
// when
StatementResponse userStatement = statementService.getUserStatements(loginUser);
// then
assertThat(userStatement.getStatementInfos()).as("거래 이력을 조회할 수 있다.").hasSize(4)
.extracting("amount", "type")
.containsExactly(
tuple(400, "이체"),
tuple(-300, "이체"),
tuple(-200, "출금"),
tuple(100, "입금")
);
assertThat(userStatement.getBalance()).as("계좌 잔액을 조회할 수 있다.").isEqualTo(1000);
}
(위 테스트 코드는 시간 설정 및 검증 코드를 제거한 테스트입니다.) 위 메서드와 plusDays() 통해 시간 설정 후, extracting에 "date" (createdAt에 yyyy-MM-dd를 적용시켜 String 형태로 저장) 를 추가해서 확인했을 때에는 의도한 대로 2024-06-08, 2024-06-07, 2024-06-06, 2024-06-05가 아닌, 전부 테스트를 실행한 시간으로(2024-06-05) 나오는 걸 확인했습니다. 일단은 date를 검증하는 로직을 빼고 테스트를 통과시켰고, postman에서 입금/출금/이체 등 실시 후 조회했을 때에도 의도한 대로 최근 거래가 위로 오도록, 날짜 형식도 의도한 대로 나온 것도 볼 수 있었습니다.
하지만 이 date를 postman이 아닌, auditing 없이 임의로 날짜를 설정한 후 테스트해 확인해보고 싶은데, 이 경우에는 어떻게 하면 될까요? auditing 잇는 상태로도 가능하다면 알려주시면 감사하겟습니다!
아래는 BaseEntity와 거래 내역을 저장하는 Statement Entity 코드입니다
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity{
@CreatedDate
private LocalDateTime createdDate;
}
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
import static jakarta.persistence.EnumType.*;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Statement extends BaseEntity{
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="from_account")
private Account from;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="to_account")
private Account to;
private int amount;
@Enumerated(STRING)
private TransactionType type;
@Builder
private Statement(Account from, Account to, int amount, transactionType type) {
this.from = from;
this.to = to;
this.amount = amount;
this.type = type;
}
public static Statement of(Account from, Account to, int amount, transactionType type){
return Statement.builder()
.from(from)
.to(to)
.amount(amount)
.type(type)
.build();
}
}
긴 글 읽어주셔서 감사합니다~
답변 2
0
안녕하세요, han 님!
2가지 방법이 있을 것 같네요 ㅎㅎ
ReflectionTestUtils를 사용하여 특정 필드의 값을 내가 원하는 형태로 조작할 수 있습니다. 다만 질문주신 경우처럼 꼭 필요한 상황에서만 사용하는 것이 좋습니다.
애초에, '거래일자'와 같이 도메인에서 아주 중요한 필드라면, createdAt 대신 별도의 컬럼으로 관리하는 것이 좋습니다. Auditing 기능으로 자동 생성되는 데이터들은, 비즈니스 레이어에서 제어할 수 없는 값이기 때문에 개발 참고용으로 활용하거나, 중요도가 낮은 케이스에 활용하는 것이 좋아요. 거래일자를 별도 컬럼으로 관리한다면 보다 테스트하기 용이한 구조를 가져갈 수 있겠죠 ㅎㅎ
도움이 되셨기를 바랍니다.
감사합니다. 🙂
0
안녕하세요, 인프런 AI 인턴입니다.
han님의 질문에 대해서는 “시간대에 따라 다른 결과를 주는 로직을 테스트하는 방법”과 매우 유사한 내용이 이전에도 논의된 적이 있어, 그 해결 방안을 참고하셔도 좋을 것 같습니다.
여기서 주요 포인트는 LocalDateTime
이나 관련 시간을 다루는 객체를 테스트 로직에서 직접 제어할 수 없을 때, 외부에서 시간 정보를 주입받아 이를 기반으로 행동을 제어하는 방식입니다. 이 방식은 시간을 포함한 외부 의존성이 있는 로직을 테스트할 때 자주 사용됩니다.
핵심적인 내용으로는:
LocalDateTime
등의 현재 시간을 바로 사용하지 않고, 시간 정보를 제공하는 별도의 클래스(TimeProvider
)를 사용하는 방법입니다.이 방법을 사용하면 @CreatedDate
등 JPA Auditing을 사용하여 자동으로 설정되는 시간 필드의 값을 테스트에서 임의로 조정할 수 있는 환경을 만들 수 있습니다. 특히 JPA Auditing 필드가 private
으로 설정되어 있어 직접 값을 변경할 수 없는 경우에 유용하게 활용될 수 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
감사합니다~