Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#101. 질문 관련(발급, 조회) 유닛테스트 작성 #103

Merged
merged 2 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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