我想测试以下方法:
@Transactional
override suspend fun updateDebtWithoutDocumentsByUserId(userId: Long, dtoIn: DebtDTO): DebtDTO {
if (dtoIn.id == null) {
throw NotFoundException("Can only update existing debt")
}
val d = debtRepository.findById(dtoIn.id) ?: throw NotFoundException("Could not find debt ${dtoIn.id}")
val updated = debtRepository.save(
d.copy(
title = dtoIn.title,
amount = dtoIn.amount,
category = dtoIn.category,
)
)
if (dtoIn.amount != d.amount) {
// update potentially existing installment plan.
// Note that this guarantees that the installment amount is kept in bounds and all installment records are updated
// accordingly.
val installmentPlan = installmentPlanRepository.findByDebtId(dtoIn.id)
if (installmentPlan != null) {
val installmentRecordIds: List<Long> = installmentRepository.findByDebtId(dtoIn.id)
.toList()
.map { it.recordId }
storeInstallmentPlan(dtoIn.id, InstallmentPlanDTO.from(installmentPlan, installmentRecordIds))
.collect() // ensure that the flow is executed.
}
}
val documents = debtDocumentRepository.findByDebtId(dtoIn.id)
.toSet()
.map { DocumentDTO.from(it) }
.associateBy { it.id!! }
return DebtDTO.from(updated, documents)
}
如果有可用的分期付款计划,则将调用方法storeInstallmentPlan(dtoIn.id, InstallmentPlanDTO.from(installmentPlan, installmentRecordIds))
。原始方法storeInstallmentPlan
已经测试过了。
如何在我想要测试的单元中模拟storeInstallmentPlan
,而不重复测试storeInstallmentPlan
?我想我必须以某种方式使用SpyK
来做到这一点,但我不知 prop 体如何做到这一点。也许有人能给我一个解决方案。
谢谢您的支持:)
这里是我的完整测试类:
@ExtendWith(MockKExtension::class)
internal class PlanServiceFeatureImplTest {
@MockK
lateinit var debtRepository: DebtRepository
@MockK
lateinit var debtDocumentRepository: DebtDocumentRepository
@MockK
lateinit var installmentPlanRepository: InstallmentPlanRepository
@MockK
lateinit var installmentRepository: InstallmentRepository
@MockK
lateinit var recordService: RecordService
@MockK
lateinit var userService: UserService
@InjectMockKs
lateinit var planService: PlanServiceFeatureImpl
@Test
fun `updateDebtWithoutDocumentsByUserId ok - no installment plan available`() {
val userId = 123L
val dtoIn = DebtDTO(
id = 1L,
title = "debt1New",
amount = BigDecimal.valueOf(2000),
category = DebtCategory.dangerous,
documents = mapOf(),
)
val oldDebt = Debt(
id = 1L,
userId = userId,
title = "debt1",
amount = BigDecimal.valueOf(1000),
category = DebtCategory.credit,
)
assertThat(dtoIn.title).isNotEqualTo(oldDebt.title)
assertThat(dtoIn.amount).isNotEqualTo(oldDebt.amount)
assertThat(dtoIn.category).isNotEqualTo(oldDebt.category)
coEvery { debtRepository.findById(dtoIn.id!!) } returns oldDebt
val doc1 = DebtDocument(
id = 11L,
debtId = dtoIn.id!!,
title = "doc1",
bytes = byteArrayOf(0x01),
)
coEvery { debtRepository.save(any()) } answers { arg(0) }
coEvery { debtDocumentRepository.findByDebtId(dtoIn.id!!) } returns flowOf(doc1)
//no installment plan available
coEvery { installmentPlanRepository.findByDebtId(dtoIn.id!!) } returns null
runBlocking {
val actual = planService.updateDebtWithoutDocumentsByUserId(userId, dtoIn)
assertThat(actual).isEqualTo(
dtoIn.copy(
documents = mapOf(doc1.id!! to DocumentDTO.from(doc1)),
)
)
}
coVerify {
debtRepository.save(withArg {
assertThat(it.id).isEqualTo(dtoIn.id)
assertThat(it.userId).isEqualTo(userId)
assertThat(it.title).isEqualTo(dtoIn.title)
assertThat(it.amount).isEqualTo(dtoIn.amount)
assertThat(it.category).isEqualTo(dtoIn.category)
})
}
}
@Test
fun `updateDebtWithoutDocumentsByUserId ok - installment plan available`() {
val userId = 123L
val dtoIn = DebtDTO(
id = 1L,
title = "debt1New",
amount = BigDecimal.valueOf(2000),
category = DebtCategory.dangerous,
documents = mapOf(),
)
val oldDebt = Debt(
id = 1L,
userId = userId,
title = "debt1",
amount = BigDecimal.valueOf(1000),
category = DebtCategory.credit,
)
assertThat(dtoIn.title).isNotEqualTo(oldDebt.title)
assertThat(dtoIn.amount).isNotEqualTo(oldDebt.amount)
assertThat(dtoIn.category).isNotEqualTo(oldDebt.category)
coEvery { debtRepository.findById(dtoIn.id!!) } returns oldDebt
val doc1 = DebtDocument(
id = 11L,
debtId = dtoIn.id!!,
title = "doc1",
bytes = byteArrayOf(0x01),
)
coEvery { debtRepository.save(any()) } answers { arg(0) }
coEvery { debtDocumentRepository.findByDebtId(dtoIn.id!!) } returns flowOf(doc1)
//installment plan available
val plan = mockk<InstallmentPlan>()
coEvery { installmentPlanRepository.findByDebtId(dtoIn.id!!) } returns plan
coEvery { installmentRepository.findByDebtId(dtoIn.id!!) } returns flowOf()
/*
Here I need to test storeInstallmentPlan / mock of it
*/
runBlocking {
val actual = planService.updateDebtWithoutDocumentsByUserId(userId, dtoIn)
assertThat(actual).isEqualTo(
dtoIn.copy(
documents = mapOf(doc1.id!! to DocumentDTO.from(doc1)),
)
)
}
coVerify {
debtRepository.save(withArg {
assertThat(it.id).isEqualTo(dtoIn.id)
assertThat(it.userId).isEqualTo(userId)
assertThat(it.title).isEqualTo(dtoIn.title)
assertThat(it.amount).isEqualTo(dtoIn.amount)
assertThat(it.category).isEqualTo(dtoIn.category)
})
}
}
@Test
fun `storeInstallmentPlan ok`(@MockK user: User) {
val debtId = 123L
val debtAmount = BigDecimal.valueOf(1000)
val installmentAmount = BigDecimal.valueOf(500)
val interval = Repeat.monthly
val firstPaymentDate = LocalDate.of(2021, 12, 27)
.atStartOfDay(Time.DEFAULT_TIME_ZONE).toOffsetDateTime()
val planDTO = InstallmentPlanDTO(
interval = interval,
firstPaymentDate = firstPaymentDate,
amount = installmentAmount,
installments = listOf()
)
val debt = Debt(
userId = 32L,
title = "debt1",
amount = debtAmount,
category = DebtCategory.credit
)
val plan = InstallmentPlan(
id = 122L,
debtId = debtId,
interval = interval,
firstPaymentDate = firstPaymentDate,
amount = installmentAmount
)
val installment1 = Installment(
id = 12L,
debtId = debtId,
recordId = 15L
)
val installment2 = Installment(
id = 13L,
debtId = debtId,
recordId = 16L
)
val installments = listOf(
WalletRecord(
userId = debt.userId,
type = RecordType.debt_rate,
amount = installmentAmount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = plan.interval,
startDate = ZonedDateTime.parse("2021-11-28T00:00+01:00[Europe/Berlin]").toOffsetDateTime(),
endDate = ZonedDateTime.parse("2021-12-27T00:00+01:00[Europe/Berlin]").toOffsetDateTime()
),
WalletRecord(
userId = debt.userId,
type = RecordType.debt_rate,
amount = installmentAmount,
title = debt.title,
category = RecordCategory.debt_rate,
repeat = plan.interval,
startDate = ZonedDateTime.parse("2021-12-28T00:00+01:00[Europe/Berlin]").toOffsetDateTime(),
endDate = ZonedDateTime.parse("2022-01-27T00:00+01:00[Europe/Berlin]").toOffsetDateTime()
)
)
every { user.tz } returns "Europe/Berlin"
coEvery { debtRepository.findById(debtId) } returns debt
//installment plan for debt with debtId exists
coEvery { installmentPlanRepository.findByDebtId(debtId) } returns plan
//update installment plan
coEvery {
installmentPlanRepository.save(planDTO.copy(amount = installmentAmount).toEntity(id = plan.id, debtId = debtId))
} returns InstallmentPlan(plan.id, debtId, interval, firstPaymentDate, installmentAmount)
//find and delete all previous installments/records for debt with debtId
coEvery { installmentRepository.findByDebtId(debtId) } returns flowOf(installment1, installment2)
coEvery { installmentRepository.deleteAllById(listOf(installment1.id!!, installment2.id!!)) } just Runs
coEvery { recordService.deleteAll(listOf(installment1.recordId, installment2.recordId)) } just Runs
//replace all new installments/records
coEvery { userService.findById(debt.userId) } returns user
coEvery { recordService.saveAll(installments) } returns flowOf()
coEvery { installmentRepository.saveAll(any<Flow<Installment>>()) } returns flowOf()
runBlocking { planService.storeInstallmentPlan(debtId, planDTO) }
coVerify { installmentPlanRepository.save(
planDTO.copy(amount = installmentAmount).toEntity(id = plan.id, debtId = debtId)
) }
coVerify { recordService.saveAll(installments) }
coVerify { installmentRepository.saveAll(any<Flow<Installment>>()) }
}
}
2条答案
按热度按时间xsuvu9jc1#
假设
storeInstallmentPlan()
是公共的,那么你可以用@SpyK
注解你正在测试的类,然后你需要在updateDebtWithoutDocumentsByUserId ok - no installment plan available
测试中模拟storeInstallmentPlan()
调用。如果您可以添加完整的测试类,那么我们可以提供一个完整的示例。
t98cgbkg2#
如何在我想要测试的单元中模拟storeInstallmentPlan,而不需要再次测试storeInstallmentPlan?
您可以监视被测试的示例。伪代码:
如果
storeInstallmentPlan
还不是公共的,你可以用@VisibileForTesting
标记它。或者,考虑在计划存在时测试调用方法的结果。当存在计划时,您可以观察到哪些副作用是调用该方法的预期结果?