티스토리 뷰
1. 개발 환경
Java 17
SpringBoot 3.2.3
2. 테스트 시에 사용된 코드
DTO
클라이언트에 반환되는 데이터를 담고 있다.
@Getter
public class GetSectionsResponseDto {
.. 생략
@Schema(description = "회고 카드 등록일", example = "2021-07-01T00:00:00")
private LocalDateTime createdDate;
}
Repository
QueryDSL을 사용하여 DB에서 데이터를 조회하고, 조회한 데이터를 프로젝션을 통해 DTO로 변환한다.
@RequiredArgsConstructor
public class SectionRepositoryCustomImpl implements SectionRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public List<GetSectionsResponseDto> getSections(Long retrospectiveId) {
return queryFactory
.select(Projections.constructor(
GetSectionsResponseDto.class,
section.id, user.id, user.username, section.content,
section.likeCnt, templateSection.sectionName, section.createdDate,
user.thumbnail, actionItem, kudosTarget
))
.from(section)
.join(section.user, user)
.leftJoin(actionItem).on(actionItem.section.id.eq(section.id)).fetchJoin()
.leftJoin(kudosTarget).on(kudosTarget.section.id.eq(section.id)).fetchJoin()
.orderBy(section.createdDate.desc())
.fetch();
}
}
테스트 코드
Repository를 검증하기 위한 단위 테스트이다.
@SpringBootTest
@Transactional
@ActiveProfiles("test")
class SectionRepositoryTest {
@Test
@DisplayName("회고보드에 작성된 모든 회고카드를 조회할 수 있다.")
void getSections() {
//given
.. 생략
//when
List<GetSectionsResponseDto> sections = sectionRepository.getSections(
retrospective.getId());
//then
.. 생략
assertThat(sections)
.extracting("sectionId", "userId", "username", "content", "likeCnt", "sectionName",
"thumbnail")
.containsExactlyInAnyOrder(
tuple(section1.getId(), user.getId(), user.getUsername(), section1.getContent(),
section1.getLikeCnt(), keepTemplateSection.getSectionName(),
user.getThumbnail()),
tuple(section2.getId(), user.getId(), user.getUsername(), section2.getContent(),
section2.getLikeCnt(), problemTemplateSection.getSectionName(),
user.getThumbnail())
);
}
}
3. 테스트의 불일치?
개발 환경에서는 테스트 검증에 문제가 없었는데, 깃허브로 코드를 올리면 CI 파이프라인에서 테스트 검증에 실패한다.
4. CI 파이프라인에 에러 로그를 출력해 보았다.
Artifact란?
GitHub Actions에서 제공하는 Artifact를 사용하여 에러 로그를 출력할 수 있도록 하였다. Artifact는 workflow 실행 중에 생성된 파일이며 테스트 결과를 보고서 형태로 출력한다.
- 빌드 결과 저장
- 테스트 결과 저장
- 로그 및 오류 보고서 저장
CI 파이프라인에 Artifact를 추가한다.
CI 파이프라인을 실행하면서 발생하는 에러를 보고서로 출력하기 위해 Artifact를 추가한다.
- if: failure() : 이전 step에서 테스트를 실패했을 경우에만 현재 step을 실행한다.
- 이전 step에서 테스트에 통과했다면, 현재 step을 무시한다.
- uses: actions/upload-artifact@v3 : Artifact를 사용한다.
- with.name : 업로드할 Artifact 이름
- with.path : Artifact 저장 경로
- name: 테스트 결과 아티팩트로 저장
if: failure()
uses: actions/upload-artifact@v3
with:
name: test-results
path: build/reports/tests/test
5. Artifact를 통해 에러 로그를 확인한다
빌드 과정에서 테스트가 실패한 이유에 대해서 확인할 수 있다.
6. 테스트 코드 수정
모든 엔티티가 객체 생성 날짜(createdDate)와 수정 날짜(updatedDate)를 공동으로 가지기 때문에 BaseEntity를 사용하고 있다. 자바에서 LocalDateTime 타입은 MySQL 환경에서 DATETIME 타입하고 매핑된다.
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime updatedDate;
}
실제로 테이블 컬럼 정보를 보면 DATETIME을 사용하고 있다. DATETIME은 "YYYY-MM-DD HH:MM:SS" 형식으로 저장된다. DATETIME(6)의 경우 소수점 이하 마이크로초 단위를 의미하므로, "YYYY-MM-DD HH:MM:SS.ffffff" 이런 형식으로 저장된다.
Artifact를 통해 LocalDateTime 검증 시에 나노초가 달라서 빌드에 실패하는 것을 확인하였다. 로컬 MySQL에서는 소수점 이하 6자리까지 사용되고 있는데, CI 파이프라인에서의 MySQL 9자리를 사용하고 있던 것이다.
expected: 2024-08-16T06:00:09.003285988 (java.time.LocalDateTime)
but was: 2024-08-16T06:00:09.003286 (java.time.LocalDateTime)
LocalDateTime 값이 서로 다른 이유는 데이터가 저장되는 시점에 나노차가 반올림되어 값이 달라지기 때문이다. 따라서 검증 시에는 나노초를 0으로 일치시켜서 해결할 수 있다. 자바에서는 withNano()를 사용하여 나노초를 수정할 수 있다.
assertThat(result.get(2).getMemoDeadLine().withNano(0))
.isEqualTo(memo3.getMemoDeadLine().withNano(0));