[Kotlin] Private Funtion ํ…Œ์ŠคํŠธ

 ๐Ÿ’ก ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ private ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฒ€์ฆํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๊ณ , ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์„ ํ•™์Šตํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

private ํ•จ์ˆ˜ ํ…Œ์ŠคํŠธ ๋ฌธ์ œ์ 

  • MonthPicker ํด๋ž˜์Šค๋Š” ์œ ํšจํ•œ ๋‹ฌ์„ ๊ฒ€์‚ฌํ•˜๊ณ , ๋ชจ๋“  ๋‹ฌ์˜ ์ผ์„ 30์ผ๋กœ ๊ฐ€์ •ํ•˜์—ฌ ํ•ฉ์„ ๊ตฌํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
  • makeDays๋ฅผ ํ†ตํ•ด์„œ ๋‚ด๋ถ€์˜ ๊ฐ€๋ณ€์ธ์ž๋กœ ๋“ค์–ด์˜จ ๊ฐ’์ด ๋ชจ๋‘ ์ •ํ™•ํ•œ ๋‹ฌ์ธ์ง€ isValid๋ฅผ ํ†ตํ•ด ๊ฒ€์‚ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
class MonthPicker() {
    fun makeDays(vararg months: Int): Int {
        if (isValid(*months)) throw IllegalArgumentException(ERROR_MONTH)
        return months.reduce { acc, month ->
            acc + month * DAY_OF_MONTH
        }
    }

    private fun isValid(vararg months: Int): Boolean {
        return months.all { it in START_MONTH .. END_MONTH }
    }

    companion object {
        private const val START_MONTH = 1
        private const val END_MONTH = 12
        private const val DAY_OF_MONTH = 30
        private const val ERROR_MONTH = "์˜ฌ๋ฐ”๋ฅธ ๋‹ฌ์ด ์•„๋‹™๋‹ˆ๋‹ค."
    }
}

  • makeDays์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, isValid ํ•จ์ˆ˜๋Š” private๋กœ ์„ ์–ธ๋˜์–ด ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

์ฑ…์ž„ ๋ถ„๋ฆฌ

  • ๊ฐ์ฒด ๋‚ด๋ถ€์— private๋กœ ์„ ์–ธ๋œ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์€, ๊ฐ์ฒด๊ฐ€ ์ž์‹ ์˜ ์ฑ…์ž„์ด ์•„๋‹Œ ์—ญํ• ๊นŒ์ง€ ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋œปํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ฑ…์ž„์„ ๊ฐ€์ ธ๊ฐ€๋„๋ก ๋ถ„๋ฆฌํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
class MonthPicker(
    private val monthValidator: MonthValidator
) {
    fun makeDays(vararg months: Int): Int {
        if (monthValidator.isValid(*months)) throw IllegalArgumentException(ERROR_MONTH)
        return months.reduce { acc, month ->
            acc + month * DAY_OF_MONTH
        }
    }

    companion object {
        private const val DAY_OF_MONTH = 30
        private const val ERROR_MONTH = "์˜ฌ๋ฐ”๋ฅธ ๋‹ฌ์ด ์•„๋‹™๋‹ˆ๋‹ค."
    }
}

class MonthValidator(){
    fun isValid(vararg months: Int): Boolean {
        return months.all { it in START_MONTH .. END_MONTH }
    }
    companion object {
        private const val START_MONTH = 1
        private const val END_MONTH = 12
    }
}

  • MonthValidator() ํด๋ž˜์Šค๊ฐ€ ํ•ด๋‹น ์ฑ…์ž„์„ ๊ฐ€์ ธ๊ฐ€๋„๋ก ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์ด๋กœ ์ธํ•ด์„œ ๊ฐ ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๋งŽ์€ ์˜์กด์„ฑ์ด ์ƒ๊ฒจ์„œ ์ฑ…์ž„์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ด ์žˆ๊ณ , ์ฑ…์ž„์„ ์ง€๋‚˜์น˜๊ฒŒ ๋ถ„๋ฆฌํ•˜๋ฉด ๋””๋ฏธํ„ฐ์˜ ๋ฒ•์น™์„ ์œ„๋ฐ˜ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธ

  • private ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด์„œ private ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” public ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
class MonthPikerTest {
    private lateinit var monthPicker: MonthPicker
    
    @BeforEach
    fun setUp() {
        monthPicker = MonthPicker()
    }
    
    @Test
    fun `test isValid`() {
        // then
        asserThrows(java.lang.IllegalArgumentException::class.java){
            monthPicker.makeDays(0)
        }
    }
}

  • ์œ„์™€ ๊ฐ™์ด private๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์ •์ƒ์ ์ธ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
    • ์„ธ๋ถ€ ๋™์ž‘์„ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•˜์ง€ ๋ชปํ•œ๋‹ค
    • ๊ตฌ์ฒด์ ์ธ ์›์ธ ๋ถ„์„์ด ์–ด๋ ต๋‹ค
    • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์˜๋ฏธ์™€ ๋ฒ”์œ„๊ฐ€ ๋ถˆํ™•์‹คํ•˜๋‹ค
    • ๋ฆฌํŒฉํ† ๋ง์— ์ทจ์•ฝํ•˜๋ฉฐ ํ…Œ์ŠคํŠธ์˜ ์˜๋ฏธ๋ฅผ ์ƒ๊ฐํ–ˆ์„ ๋•Œ ์ž˜๋ชป๋  ์ˆ˜ ์žˆ๋‹ค

mockk ํ™œ์šฉ

  • Mockk๋Š” ์ฝ”ํ‹€๋ฆฐ์„ ์œ„ํ•œ Mock ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.
  • Repository, Api ํ˜ธ์ถœ ๋ฉ”์†Œ๋“œ์™€ ๊ฐ™์ด ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด๋ฅผ mockingํ•˜์—ฌ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ๊ณ , private ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
class MonthPickerTest {

    @Test
    fun `test makeDays with valid months`() {
        // MonthValidator๋ฅผ ๋ชจํ‚น
        val monthValidator = mockk<MonthValidator>()

        // ๋ชจํ‚น๋œ MonthValidator๊ฐ€ ํ•ญ์ƒ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค์ •
        every { monthValidator.isValid(anyVararg()) } returns true

        // MonthPicker์— ๋ชจํ‚น๋œ MonthValidator๋ฅผ ์ฃผ์ž…
        val monthPicker = MonthPicker(monthValidator)

        // ์œ ํšจํ•œ ์›”์ผ ๊ฒฝ์šฐ ์ •์ƒ์ ์œผ๋กœ ๊ณ„์‚ฐ
        val result = monthPicker.makeDays(1, 2, 3)

        // 1, 2, 3๊ฐœ์›”์˜ ํ•ฉ๊ณ„ * 30์ผ = 180์ผ
        assertEquals(180, result)
    }

    @Test
    fun `test makeDays with invalid months`() {
        // MonthValidator๋ฅผ ๋ชจํ‚น
        val monthValidator = mockk<MonthValidator>()

        // ๋ชจํ‚น๋œ MonthValidator๊ฐ€ ํ•ญ์ƒ false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค์ •
        every { monthValidator.isValid(anyVararg()) } returns false

        // MonthPicker์— ๋ชจํ‚น๋œ MonthValidator๋ฅผ ์ฃผ์ž…
        val monthPicker = MonthPicker(monthValidator)

        // ์œ ํšจํ•˜์ง€ ์•Š์€ ์›”์ผ ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ
        val exception = assertThrows<IllegalArgumentException> {
            monthPicker.makeDays(13, 14)
        }

        // ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€ ํ™•์ธ
        assertEquals("์˜ฌ๋ฐ”๋ฅธ ๋‹ฌ์ด ์•„๋‹™๋‹ˆ๋‹ค.", exception.message)
    }
  • spyk๋กœ ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ชจํ‚นํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • every๋กœ private ๋ฉ”์„œ๋“œ ๋ชจํ‚น
    • every { monthPicker"๋ฉ”์„œ๋“œ๋ช…” } returns false or true
    • ์œ„์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ํ•ญ์ƒ false๋‚˜ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋ฅผ ํ†ตํ•ด ์›”์ด ์œ ํšจํ•˜์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ•˜๋„๋ก ์„ค์ •ํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  • verify๋กœ ํ˜ธ์ถœ ๊ฒ€์ฆ
    • isValid ๋ฉ”์„œ๋“œ๊ฐ€ ํŠน์ • ์ธ์ž์™€ ํ•จ๊ป˜ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ

  • ์œ„ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ private ๋ฉ”์„œ๋“œ๋ฅผ ๋ชจํ‚นํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๊ฐ„์ ‘์ ์œผ๋กœ public ๋ฉ”์„œ๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.