내가 원하는 것은 아주 간단한 테스트였다.
given에서는 user에게 연관된 게시글을 총 15개 만들어서 저장하는데, 10개는 모두 같은 날짜의 게시글이고 나머지 5개는 이전 5일간의 게시글로 저장하는 것이다.
이후 이를 조회하면 날짜별로 집계되어 6개의 값을 가진 List가 반환되어야 한다.
즉,
Select ( count(article) , article.created_date)
from (article)
where article.id = userId
group by article.created_date
having article.created_date between(start, end)
이런 형태를 가진 아주 단순한 querydsl 쿼리문에 대한 테스트다.
근데 온갖 짓을 해봐도 테스트에 실패했다.
결과가 그냥 [ 15, 2022-11-23 ] 이렇게 grop by되지 않고 한 번에 카운트되어서 출력된다.
최근 내 쿼리 작성 능력이 아주 형편 없다는 것을 깨닫고 자신감이 떨어져 이 단순한 쿼리도 잘못 작성한 것인가에 대한 고민에 빠졌지만, 기능 테스트는 멀쩡하게 통과했다.
영문 모를 결과에 한참을 해메다가 로그를 읽어봤는데..
나의 노력이 무색하게 모든 쿼리가 오늘 날짜를 기준으로 날아가고 있었다.
@CreatedDate의 원리를 찾아봤는데, AOP를 이용해서 영속화 시점에 날짜를 직접 만들어서 넣어주고 저장 직전에 이를 반영한다.
그러면 그냥 저장한 뒤에 EntityManager를 flush, clear해서 강제로 중단시키고 다시 불러와서 Reflection으로 수정해버리면 되는 거 아닐까? 했지만 안된다.
이건 이유를 모르겠다.
단순히 값을 System.out.println으로 찍어보면 날짜가 바뀐 것으로 나오는데, 실제 update 쿼리는 날아가지 않는다.
단순히 JpaAuditing 소스코드만 봤을 때는 원인을 알 수 없었지만 일단 바쁘기 때문에 이건 포기하고 빠르게 다른 방법을 찾아냈다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@MockBean
DateTimeProvider dateTimeProvider;
@SpyBean
AuditingHandler auditingHandler;
@BeforeEach
void setup() throws Exception {
MockitoAnnotations.openMocks(this);
auditingHandler.setDateTimeProvider(dateTimeProvider);
}
|
cs |
우선 DateTimeProvider한테 MockBean을 주입하고, AuditingHandler를 SpyBean으로 넣어준다.
그리고 스파이로 잠입시킨 auditingHandler를 이용해 DateTime을 제공하는 DateTimeProvider를 JpaAuditing에서 사용하는 날짜 제공자로 주입하면 된다.
그 뒤는 일반적인 테스팅 방법과 똑같다.
BDDMockito라면 given()을, 아니면 when()을 이용해서 모킹해주면 끝이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Test
void getUserArticlesDataBetween() {
// given
User user = User.builder().build();
entityManager.persist(user);
BDDMockito.given(dateTimeProvider.getNow()).willReturn(Optional.of(LocalDateTime.of(2022,1,6,0,0,0)));
for (int i=0; i<10; i++) {
Article article = Article.builder().user(user).build();
article.injectUserForMapping(user);
entityManager.persist(article);
}
for (int i=1; i<=5; i++) {
BDDMockito.given(dateTimeProvider.getNow()).willReturn(Optional.of(LocalDateTime.of(2022,1,i,0,0,0)));
Article article = Article.builder().user(user).build();
article.injectUserForMapping(user);
entityManager.persist(article);
}
// when
List<ActivityDto.Temporary> result = userQueryRepository.getUserArticlesDataBetween(january1st, december31st, user.getId());
// then
assertThat(result.size()).isEqualTo(6);
}
|
cs |
실제 사용 코드는 위와 같다.
이렇게 작성하면 가독성이 떨어지니까 Optional.of(LocalDateTime ~~~ )을 반환하는 메서드를 별도로 만드는 게 좋을 것 같다.
이런 걸로 몇 시간을 날린 건지.. 그래도 포기하고 대충 넘기지 않고 늘 끝까지 찾아서 해결하는 내 자신이 기특하다
ㅋㅋㅎ
'Language & Framework > 삽질기록' 카테고리의 다른 글
삽질 기록(16) 이런 귀여운 스테이터스 코드를 보고 그냥 지나갈 수가 없었다. 418 I'm a teapot 적용하기. (4) | 2022.12.15 |
---|---|
삽질 기록 (15) 한 번에 여러 Entity를 저장해야한다면? 벌크벌크 BulkInsert (2) | 2022.12.12 |
삽질기록(12) 레디스 적용된 코드 어떻게든 꾸역꾸역 단위테스트 하기 (0) | 2022.12.01 |
삽질 기록 (12) 단위테스트 분해 조립하기 (0) | 2022.11.26 |
삽질 기록 (11) 로컬에서는 잘만 되는 WebSocket 채팅 EC2에서는 외않뒈? (0) | 2022.11.23 |