์ด๋ฒ์ ์ฝํ๋ง์ผ๋ก ๊ฐ๋ฐํ๋ฉด์ unit test๋ฅผ mockito๋ฅผ ํ์ฉํ์ฌ ์งํํ๊ฒ ๋์๋ค. unit test์ ๊ดํด ์ฐพ์๋ณด๋ ์ค kotlin์์ ์ฌ์ฉํ๋ mockk ๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๊ฒฌํ์๋ค. ์ผ๋ฐ์ ์ผ๋ก ๋ง์ด ์ฌ์ฉ๋๋ mockito-core ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ณ ๋ฏผ ํด๋ณด์์ง๋ง kotlin์ค๋ฝ๊ฒ ๊ฐ๋ฐํ ์ ์๋ mockk์ ๋ ๋๋ฆฌ๊ฒ ๋์๋ค. ๊ทธ๋ ๊ฒ mockk ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ํ ์คํธ๋ฅผ ์งํํ๊ณ Service Layer ๊ณ์ธต์ ๊ดํด unit test๋ฅผ ํ์๊ณ ์ด๋ฅผ ์ ๋ฆฌํ์ฌ ๋ณด์๋ค.
testImplementation("io.mockk:mockk:1.12.0")
์ ๋ฒ์ (1.13.3)์ด ์๋ ๊ตฌ ๋ฒ์ ์ฌ์ฉ ์ด์
์ ๋ฒ์ ์ฌ์ฉ ํ ํ์ฌ ์ฌ์ฉ์ค์ธ ์ฝํ๋ฆฐ ๋ฒ์ ๊ณผ ์ถฉ๋ํ์ฌ ๊ตฌ ๋ฒ์ ์ด์ฉ
mockk ์ ์ด์ฉํ ๊ฐ์ฒด ์์ฑ์ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ์์ด ์กด์ฌํ๋ค. ๊ทธ์ค์์๋ Junit5 ๋ฅผ ์ฌ์ฉํ ์ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฅ์ด ์กด์ฌํด์ ํด๋น ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
@ExtendWith(MockKExtension::class)
internal class PromotionServiceTest {
@MockK
private lateinit var promotionRepoMock: PromotionRepo
@InjectMockKs
private lateinit var promotionServiceMock: PromotionService
์์ ๊ฐ์ด ์ฌ์ฉํ ์๋น์ค์ ๋ ํฌ์งํ ๋ฆฌ์ ๋ํด์ ์์กด์ฑ์ ๋ถ์ฌํ๊ณ ์๋ค. ์ฌ์ฉํ ์ด๋ ธํ ์ด์ ์ ๋ํ์ฌ ์ ๋ฆฌํ์ฌ ๋ณด์๋ค.
@ExtendWith(MockKExtendsion::class)
@Mockk
@InjectMockKs
์ ์ฅ ๊ธฐ๋ฅ์ ํ ์คํธํ๊ธฐ ์ํด์ ์๋์ ๊ฐ์ด ๋ก์ง์ ์์ฑํ์๋ค. ํฌ๊ฒ given, when, then์ผ๋ก ๋๋์ด ๋ณด์๋ค.
@Test
fun save() {
// given
val request = PromotionSaveRequest(
name = "test01",
endTime = LocalDate.parse("2022-10-10")
)
val promotion = Promotion(
name = "test01",
endTime = LocalDate.parse("2022-10-10")
)
every{promotionRepoMock.save(any())}returns promotion
// when
val save = promotionServiceMock.save(request)
// then
verify(exactly = 1){promotionRepoMock.save(any())}
// id ๊ฐ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ์ง์ ํ ์ ์์์ผ๋ก 0์ด๋ ์ ๋ฐ์ ์๋ค.
assertEquals("id: 0๊ฐ ์ ์ฅ๋์์ต๋๋ค.", save)
}
์ญ์ ๊ธฐ๋ฅ์ mockk์ ์ฌ์ฉํ๋ฉด์ ๋ง์ ์๊ฐ์ ํ๊ฒ๋์๋ค. ์ค์ ๋ก DB์ ์ ์ํด์ ๋ฐ์ดํฐ๋ฅผ ์กฐํ ํ ์ ๊ฑฐํ๋ ๊ฒ์ด์๋๋ผ mocking ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ง์ด ๊ฒ ์ฒ๋ผ ํ๋ ๋๋์ด๋ผ ํ ์คํธ ํ์ ์ค์ ๋์์ ๋ ๋ค๋ฅผ ์ ์๋ค๊ณ ๋๊ปด์ก๋ค.
์ญ์ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉด์ ์ด๋ฒ์๋ capture ๋ผ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์๋ค. capture๋ฅผ ์ฌ์ฉํ ์ด์ ๋ ์ญ์ ๋ฉ์๋๊ฐ ์คํ ๋ ๋ ์ญ์ ํ ๊ฐ์ฒด๊ฐ ์ธ์๋ก ์ ๋๋ก ๋ค์ด๊ฐ๋์ง ํ์ธํ๊ธฐ ์ํจ์ด๋ค.
capture
@Test
fun delete() {
// given
val promotion = Promotion(
name = "test01",
endTime = LocalDate.parse("2022-10-10")
)
val slot = slot<Promotion>()
// when
every{promotionRepoMock.findById(1L).get()}returns promotion
every{promotionRepoMock.delete(capture(slot))}justruns
promotionServiceMock.delete(1L)
verify{promotionRepoMock.findById(1L).get()}
verify{promotionRepoMock.delete(any())}
assertEquals(promotion.name, slot.captured.name)
assertEquals(promotion.endTime, slot.captured.endTime)
}
io.mockk.MockKException: no answer found for
์ ์๋ฌ๋ ํธ์ถ ๋์์ ๋ํ ์คํ ์ ์๋ฅผ ํ์ง ์์์ ๋ฐ์ํ ์๋ฌ์ด๋ค.
์ ์๋ฌ๋ฅผ ํด๊ฒฐ ํ๊ธฐ ์ํด์ relaxed mock์ ์ฌ์ฉํ ์ ์๋ค.
// ์์
val mock = mockk<Example>(relaxed = true)
mock.someMethod(1) // --> 0 ๋ฆฌํด
์๋ฌ ํด๊ฒฐ์ฑ ์ ์๋ ๋ธ๋ก๊ทธ ์ฐธ์กฐ
https://javacan.tistory.com/entry/kotlin-mock-framework-mockk-intro
Only one matching call to PromotionRepo(#1)/save(Any) happened, but arguments are not matching
every { promotionRepoMock.save(any()) } returns promotion
every๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌํด ๊ฐ์ ์ง์ ํ ๋ ์ธ์ ๊ฐ์ผ๋ก any()๋ฅผ ์ฌ์ฉํ์๋๋ ์์๊ฐ์ ์๋ฌ๋ฅผ ๋ง์ฃผํ์๋ค. any()์ ๊ฒฝ์ฐ ์ถ์ธก์ผ๋ก ์ฌ์ฉ ๋๋ ํจ์์ด๊ณ eq()๊ฐ ์๋์ผ๋ก ์ฌ์ฉ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
์ ์ฌ์ดํธ๋ฅผ ์ด์ฉํด์ ์ธ์ ๊ฐ์ ํ์ ๋ง ํ์ธํ๋๋ก ๋ณ๊ฒฝํ์๋ค.