직접 작성한 쿼리문을 테스트 하기 위해서 리포지토리 테스트를 작성했다.
@Repository
class BoardQueryDslRepositoryImpl : QueryDslSupport(), BoardQueryDslRepository {
private val board = QBoardEntity.boardEntity
private val post = QPostEntity.postEntity
private val category = QCategoryEntity.categoryEntity
override fun findBoards(): List<BoardRowDto> {
return queryFactory
.select(
QBoardRowDto(
board.id,
board.boardName
)
)
.from(board)
.orderBy(board.id.asc())
.fetch()
}
override fun findStreamers(): List<StreamerBoardRowDto> {
return queryFactory
.select(
QStreamerBoardRowDto(
board.id,
board.boardUrl,
board.boardName,
)
)
.from(board)
.where(board.boardType.eq(BoardType.STREAMER_BOARD))
.orderBy(board.id.asc())
.fetch()
}
override fun findBoardStatusToInactive(): List<Long> {
val checkInactiveDate = LocalDateTime.now().minusDays(8)
return queryFactory.select(board.id).distinct()
.from(post)
.leftJoin(board)
.on(board.id.eq(post.board.id))
.where(post.modifiedAt.loe(checkInactiveDate), board.boardType.eq(BoardType.STREAMER_BOARD))
.fetch()
}
override fun updateBoardStatusToInactive(inactiveBoardIdList: List<Long>) {
queryFactory
.update(board)
.set(board.boardActStatus, BoardActStatus.INACTIVE)
.where(board.id.`in`(inactiveBoardIdList))
.execute()
}
override fun findActiveStatusBoards(): List<BoardRowDto> {
return queryFactory
.select(
QBoardRowDto(
board.id,
board.boardName
)
).from(board)
.where(board.boardActStatus.eq(BoardActStatus.ACTIVE))
.orderBy(board.id.asc())
.fetch()
}
}
먼저 리포지토리 테스트를 위한 데이터베이스를 설정했다.
spring:
config:
activate:
on-profile: test
datasource:
url: jdbc:h2:mem:test;MODE=MySQL;
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
show-sql: true
hibernate:
ddl-auto: create-drop
properties:
hibernate:
format-sql: true
highlight_sql: true
use_sql_comments: true
default_batch_fetch_size: 1000
open-in-view: false
보통 GIVEN 절에서 데이터를 설정할 때 모든 메서드에서 공동으로 사용하는 데이터가 있을 경우 BeforeEach를 활용해 중복을 제거할 수 있다. 하지만 현 상홯에서는 부적합하다는 것을 확인해 각 메서드마다 GIVEN 절에서 객체들을 따로 생성했다.
@DataJpaTest
@ActiveProfiles("test")
@Import(value = [QueryDslConfig::class, BaseTimeEntityConfig::class, BaseEntityConfig::class])
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class BoardQueryDslRepositoryImplTest @Autowired constructor(
private val boardRepository: CustomBoardRepository,
private val testEntityManager: TestEntityManager
) {
@Test
fun sampleTest() {
boardRepository.save(
BoardEntity(
socialMember = SocialMemberEntity(
email = "test@gmail.com",
nickname = "test",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
),
boardName = "test",
boardUrl = "boardUrl",
boardType = BoardType.ZZIRIRIT_BOARD
)
)
testEntityManager.flush()
}
@Test
fun `게시판 전체가 조회되는지 확인`() {
// GIVEN
val socialMember = SocialMemberEntity(
email = "test@gmail1.com",
nickname = "test1",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
)
val DEFAULT_BOARD_LIST = listOf(
BoardEntity(
socialMember = socialMember,
boardName = "test1",
boardUrl = "boardUrl1",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test2",
boardUrl = "boardUrl2",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test3",
boardUrl = "boardUrl3",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test4",
boardUrl = "boardUrl4",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test5",
boardUrl = "boardUrl5",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test6",
boardUrl = "boardUrl6",
boardType = BoardType.STREAMER_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test7",
boardUrl = "boardUrl7",
boardType = BoardType.STREAMER_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test8",
boardUrl = "boardUrl8",
boardType = BoardType.STREAMER_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test9",
boardUrl = "boardUrl9",
boardType = BoardType.STREAMER_BOARD,
boardActStatus = BoardActStatus.ACTIVE
),
BoardEntity(
socialMember = socialMember,
boardName = "test10",
boardUrl = "boardUrl10",
boardType = BoardType.STREAMER_BOARD,
boardActStatus = BoardActStatus.ACTIVE
)
)
boardRepository.saveAllAndFlush(DEFAULT_BOARD_LIST)
// WHEN
val result = boardRepository.findBoards()
// THEN
result.size shouldBe 10
result[0].boardName shouldBe "test1"
result[9].boardName shouldBe "test10"
}
@Test
fun `스트리머 게시판 전체가 조회되는지 확인`() {
// GIVEN
val socialMember = SocialMemberEntity(
email = "test@gmail1.com",
nickname = "test1",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
)
val DEFAULT_BOARD_LIST = listOf(
BoardEntity(
socialMember = socialMember,
boardName = "test1",
boardUrl = "boardUrl1",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
...
)
boardRepository.saveAllAndFlush(DEFAULT_BOARD_LIST)
// WHEN
val result = boardRepository.findStreamers()
// THEN
result.size shouldBe 5
result[4].streamerNickname shouldBe "test10" // 스트리머의 닉네임이 게시판의 이름이도록 정책 세움
}
@Test
fun `비활성화 상태로 변경시킬 게시판의 아이디들을 조회하는지 확인`() {
// GIVEN & WHEN
val socialMember = SocialMemberEntity(
email = "test@gmail1.com",
nickname = "test1",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
)
val DEFAULT_BOARD_LIST = listOf(
BoardEntity(
socialMember = socialMember,
boardName = "test1",
boardUrl = "boardUrl1",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
...
)
boardRepository.saveAllAndFlush(DEFAULT_BOARD_LIST)
val result = boardRepository.findBoardStatusToInactive()
// THEN
result.size shouldNotBe null // 비활성화 상태로 변경시킬 게시판의 아이디를 찾지 못하면 0이 size 가 0이라도 반환되기 때문에 null 이 아니면 메서드 동작 확인 성공.
}
@Test
fun `게시판이 비활성화 상태로 업데이트 되는지 확인`() {
// GIVEN
val socialMember = SocialMemberEntity(
email = "test@gmail1.com",
nickname = "test1",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
)
val DEFAULT_BOARD_LIST = listOf(
BoardEntity(
socialMember = socialMember,
boardName = "test1",
boardUrl = "boardUrl1",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
...
)
val boards = boardRepository.saveAllAndFlush(DEFAULT_BOARD_LIST)
// WHEN
val boardIds = boards.map { it.id!! }
// THEN
boardRepository.updateBoardStatusToInactive(boardIds) // 반환 타입이 unit 이라서 행위를 테스트
}
@Test
fun `활성화 상태의 게시판이 조회되는지 확인`() {
// GIVEN
val socialMember = SocialMemberEntity(
email = "test@gmail1.com",
nickname = "test1",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
)
val DEFAULT_BOARD_LIST = listOf(
BoardEntity(
socialMember = socialMember,
boardName = "test1",
boardUrl = "boardUrl1",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
...
)
boardRepository.saveAllAndFlush(DEFAULT_BOARD_LIST)
// WHEN
val result = boardRepository.findActiveStatusBoards()
// THEN
result.size shouldBe 8
}
}
이전에 작성했던 코드에서 companion object 를 이용해 테스트 메서드 내에서 동일한 데이터를 모두 사용할 수 있게 작성했는데 오류를 만나게 됐다.
@Test
fun `게시판 전체가 조회되는지 확인`() {
// GIVEN
boardRepository.saveAllAndFlush(DEFAULT_BOARD_LIST)
// WHEN
val result = boardRepository.findBoards()
// THEN
result.size shouldBe 10
result[0].boardName shouldBe "test1"
result[9].boardName shouldBe "test10"
}
...
companion object {
companion object {
val socialMember = SocialMemberEntity(
email = "test@gmail1.com",
nickname = "test1",
provider = OAuth2Provider.TEST,
providerId = "providerId",
memberRole = MemberRole.ADMIN
)
val DEFAULT_BOARD_LIST = listOf(
BoardEntity(
socialMember = socialMember,
boardName = "test1",
boardUrl = "boardUrl1",
boardType = BoardType.ZZIRIRIT_BOARD,
boardActStatus = BoardActStatus.INACTIVE
),
...
org.springframework.dao.DataIntegrityViolationException: could not execute statement [Referential integrity constraint violation: "FK3N752SW9EC3V9S2V4MMJ3710N: PUBLIC.BOARD FOREIGN KEY(SOCIAL_MEMBER_ID) REFERENCES PUBLIC.SOCIAL_MEMBER(ID) (CAST(1 AS BIGINT))"; SQL statement:
에러 메시지를 확인해보니 companion object 에서 만든 Board 객체 내 SocialMember 객체를 @Test 메서드 실행 시 Board 안에서 찾을 수 없기 때문이었다.
Board 테이블이 참조하는 SocialMember 의 컬럼 값과 참조되는 SocialMember 테이블의 컬럼 값이 동일하지 않아 발생한 문제였다.
참조 무결성 제약조건이 위배된 상황을 확인하고 난 후 테스트 메서드 내에서 모두 같은 데이터를 쓰지만 데이터를 격리해 픽스쳐를 공유하지 않기로 결정했다.
생성되는 데이터를 메서드 내에서 바로 확인할 수 있다는 이점도 생겼다.
👍