Skip to content

Commit

Permalink
Feat/#101. 질문 관련(발급, 조회) 유닛테스트 작성 (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
RokwonK authored Aug 20, 2024
1 parent bf64e31 commit 7647684
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 104 deletions.
2 changes: 1 addition & 1 deletion adevspoon-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies {
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")

// developmentOnly("org.springframework.boot:spring-boot-docker-compose")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,32 @@ class QuestionOpenDomainService(
)
}

// 정책 - 1일 1회 Random 발급
@Transactional(propagation = Propagation.MANDATORY)
fun issueQuestion(memberId: Long, today: LocalDate): QuestionInfo {
// 정책 - 1일 1회 Random 발급
logger.info("질문발급 : memberId($memberId), today($today)")
logger.info("질문발급 시작 : memberId($memberId), today($today)")
val user = getMember(memberId)
val selectedCategoryIds = userCustomizedQuestionCategoryRepository.findAllSelectedCategoryIds(user)

// 질문 선정
val categoryIds = userCustomizedQuestionCategoryRepository.findAllSelectedCategoryIds(user)
.takeIf { it.isNotEmpty() }
?: questionCategoryRepository.findAllIds()
val allQuestionIds = questionRepository.findAllQuestionIds(categoryIds)
val issuedQuestionIds = questionOpenRepository.findAllIssuedQuestionIds(user)
val issuableQuestionIds = (allQuestionIds - issuedQuestionIds)
.takeIf { it.isNotEmpty() }
?: throw QuestionExhaustedException()
val selectedQuestionId = issuableQuestionIds.random()

val alreadyIssuedQuestionIds = questionOpenRepository.findAllIssuedQuestionIds(user)
val candidateIssuableQuestionIds =
(questionRepository.findAllQuestionIds(selectedCategoryIds) - alreadyIssuedQuestionIds)
.takeIf { it.isNotEmpty() }
?: throw QuestionExhaustedException()

val issuedQuestionId = candidateIssuableQuestionIds.random()
val question = getQuestion(issuedQuestionId)
val issuedQuestion = QuestionOpenEntity(user = user, question = question, openDate = today.atStartOfDay())
// 질문 발급
val question = getQuestion(selectedQuestionId)
val questionOpen = QuestionOpenEntity(user = user, question = question, openDate = today.atStartOfDay())
questionOpenRepository.save(questionOpen)

questionOpenRepository.save(issuedQuestion)
// 유저 정보 업데이트
user.increaseQuestionCnt()

return makeQuestionInfo(issuedQuestion, candidateIssuableQuestionIds.size == 1)
return makeQuestionInfo(questionOpen, issuableQuestionIds.size == 1)
}

private fun getCategoryList(categoryNameList: List<String>): List<QuestionCategoryEntity> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.adevspoon.domain.annotation

import io.mockk.junit5.MockKExtension
import org.junit.jupiter.api.extension.ExtendWith

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@ExtendWith(MockKExtension::class)
annotation class UnitTest()
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.adevspoon.domain.techQuestion.service

import com.adevspoon.domain.annotation.UnitTest
import com.adevspoon.domain.fixture.MemberFixture
import com.adevspoon.domain.fixture.QuestionFixture
import com.adevspoon.domain.member.domain.UserEntity
Expand All @@ -13,42 +14,31 @@ import com.adevspoon.domain.techQuestion.repository.QuestionOpenRepository
import com.adevspoon.domain.techQuestion.repository.QuestionRepository
import com.adevspoon.domain.techQuestion.repository.UserCustomizedQuestionCategoryRepository
import io.mockk.every
import io.mockk.mockk
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.MockK
import io.mockk.verify
import org.assertj.core.api.Assertions.*
import org.junit.jupiter.api.Test

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.springframework.data.repository.findByIdOrNull
import java.time.LocalDate
import java.time.LocalDateTime

@UnitTest
class QuestionDomainServiceUnitTest {
private val questionCategoryRepository = mockk<QuestionCategoryRepository>()
private val questionRepository = mockk<QuestionRepository>()
private val questionOpenRepository = mockk<QuestionOpenRepository>()
private val userRepository = mockk<UserRepository>()
private val userCustomizedQuestionCategoryRepository = mockk<UserCustomizedQuestionCategoryRepository>()
private val questionOpenDomainService = mockk<QuestionOpenDomainService>()

private lateinit var questionDomainService: QuestionDomainService

@BeforeEach
fun setup() {
questionDomainService = QuestionDomainService(
questionCategoryRepository,
questionRepository,
questionOpenRepository,
userRepository,
userCustomizedQuestionCategoryRepository,
questionOpenDomainService
)
}
@MockK private lateinit var questionCategoryRepository: QuestionCategoryRepository
@MockK private lateinit var questionRepository: QuestionRepository
@MockK private lateinit var questionOpenRepository: QuestionOpenRepository
@MockK private lateinit var userRepository: UserRepository
@MockK private lateinit var userCustomizedQuestionCategoryRepository: UserCustomizedQuestionCategoryRepository
@MockK private lateinit var questionOpenDomainService: QuestionOpenDomainService

@InjectMockKs private lateinit var questionDomainService: QuestionDomainService

@Nested
inner class GetQuestion {
inner class GetQuestionUnitTests {
private lateinit var user: UserEntity
private lateinit var question1: QuestionEntity
private lateinit var question2: QuestionEntity
Expand All @@ -67,7 +57,7 @@ class QuestionDomainServiceUnitTest {
}

@Test
fun `SCCESS - 문제 가져오기 성공`() {
fun `SCCESS - 발급 받은 문제를 문제ID를 이용해 가져온다`() {
// given
val issuedQuestion = QuestionFixture.createQuestionOpen(1, question1, user = user)
every { questionOpenRepository.findByQuestionAndUser(question1, user) } returns issuedQuestion
Expand All @@ -76,26 +66,27 @@ class QuestionDomainServiceUnitTest {
val question = questionDomainService.getQuestion(user.id, question1.id)

// then
assertEquals(question.questionId, question1.id)
assertThat(question.questionId)
.isEqualTo(question1.id)

verify { questionOpenRepository.findByQuestionAndUser(question1, user) }
}

@Test
fun `FAIL - 발급 받지 않은 문제 요청`() {
fun `FAIL - 발급 받지 않은 문제 요청 시 예외가 발생한다`() {
// given
every { questionOpenRepository.findByQuestionAndUser(question1, user) } returns null

// when, then
assertThrows<QuestionNotOpenedException> {
questionDomainService.getQuestion(user.id, question1.id)
}
assertThatThrownBy { questionDomainService.getQuestion(user.id, question1.id) }
.isInstanceOf(QuestionNotOpenedException::class.java)

verify { questionOpenRepository.findByQuestionAndUser(question1, user) }
}
}

@Nested
inner class GetOrCreateTodayQuestion {
inner class GetOrCreateTodayQuestionUnitTests {
private lateinit var user: UserEntity
private lateinit var question1: QuestionEntity
private lateinit var question2: QuestionEntity
Expand All @@ -112,38 +103,48 @@ class QuestionDomainServiceUnitTest {
}

@Test
fun `SUCCESS - 문제 발급 & 응답`() {
fun `SUCCESS - 오늘자 문제를 아직 발급받지 않았다면 문제를 발급 후 응답한다`() {
// given
val today = LocalDate.now()
val yesterdayDateTime = today.atStartOfDay().minusDays(1)
val latestIssuedQuestion =
QuestionFixture.createQuestionOpen(
1,
question1,
id = 1,
question = question1,
user = user,
openDate = LocalDateTime.now().minusDays(1)
openDate = yesterdayDateTime
)
val newIssuedQuestionInfo = QuestionFixture.createQuestionInfo(questionId = question2.id)
every { questionOpenRepository.findLatestWithQuestionAndAnswer(user) } returns latestIssuedQuestion
every { questionOpenDomainService.issueQuestion(user.id, today) } returns newIssuedQuestionInfo

// when
val questionInfo = questionDomainService.getOrCreateTodayQuestion(GetTodayQuestion(user.id, today))

assertEquals(questionInfo.questionId, question2.id)
// then
assertThat(questionInfo.questionId)
.isEqualTo(question2.id)

verify { questionOpenRepository.findLatestWithQuestionAndAnswer(user) }
verify { questionOpenDomainService.issueQuestion(any(), any()) }
}

@Test
fun `SUCCESS - 기존 문제 응답`() {
fun `SUCCESS - 이미 오늘자 문제를 발급 받았다면 해당 문제를 응답한다`() {
// given
val today = LocalDate.now()
val latestIssuedQuestion =
QuestionFixture.createQuestionOpen(1, question1, user = user, openDate = LocalDateTime.now())
QuestionFixture.createQuestionOpen(1, question1, user = user, openDate = today.atStartOfDay())
val newIssuedQuestionInfo = QuestionFixture.createQuestionInfo(questionId = question2.id)
every { questionOpenRepository.findLatestWithQuestionAndAnswer(user) } returns latestIssuedQuestion
every { questionOpenDomainService.issueQuestion(any(), any()) } returns newIssuedQuestionInfo

val questionInfo = questionDomainService.getOrCreateTodayQuestion(GetTodayQuestion(1, LocalDate.now()))
// when
val questionInfo = questionDomainService.getOrCreateTodayQuestion(GetTodayQuestion(1, today))

assertEquals(questionInfo.questionId, question1.id)
// then
assertThat(questionInfo.questionId)
.isEqualTo(question1.id)

verify { questionOpenRepository.findLatestWithQuestionAndAnswer(user) }
verify(exactly = 0) { questionOpenDomainService.issueQuestion(any(), any()) }
Expand Down Expand Up @@ -176,7 +177,7 @@ class QuestionDomainServiceUnitTest {
}

@Test
fun `SUCCESS - 사용자의 문제 카테고리 가져오기 (고갈, 선택 포함)`() {
fun `SUCCESS - 선택 여부와 고갈 여부를 포함한 문제 카테고리 정보를 모두 가져온다`() {
// given
every { questionCategoryRepository.findAll() } returns listOf(
questionCategory1,
Expand All @@ -201,16 +202,13 @@ class QuestionDomainServiceUnitTest {
val categories = questionDomainService.getQuestionCategories(user.id).sortedWith(compareBy { it.id })

// then
val expected = mutableListOf(
QuestionFixture.createQuestionCategoryInfo(id = 1, depleted = true, selected = true),
QuestionFixture.createQuestionCategoryInfo(id = 2, depleted = false, selected = true),
QuestionFixture.createQuestionCategoryInfo(id = 3, depleted = false, selected = false)
)

expected.forEachIndexed { idx, info ->
assertEquals(categories[idx].depleted, info.depleted)
assertEquals(categories[idx].selected, info.selected)
}
assertThat(categories).hasSize(3)
.extracting("id", "depleted", "selected")
.containsExactlyInAnyOrder(
tuple(1L, true, true),
tuple(2L, false, true),
tuple(3L, false, false)
)
}
}
}
Loading

0 comments on commit 7647684

Please sign in to comment.