#1 Test double
fun main() {
// Dependency (Test double) ์ธ์คํด์ค ์ ์ธ
val testDouble = TestDouble()
// Dependent ์ธ์คํด์ค ์ ์ธ
val dependent = Dependent(testDouble)
// Unit test ์งํ
if (dependent.doMathOperation(10) == 55) {
println("ํ
์คํธ ์ฑ๊ณต")
} else {
println("ํ
์คํธ ์คํจ")
}
}
class Dependent(private val mathCalculator: OriginalDependency) {
fun doMathOperation(n: Int): Int {
return mathCalculator.sumFromOneToN(n)
}
}
open class OriginalDependency {
open fun sumFromOneToN(n: Int): Int {
var sum = 0
for (i: Int in 1..n) {
sum += i
}
return sum
}
}
class TestDouble : OriginalDependency() { // Dependency class for testing.
override fun sumFromOneToN(n: Int): Int {
return 55
}
}
์ด๋ค ํ๋ก์ ํธ๋ Dependent - Dependency ๊ด๊ณ๋ก ์ด๋ฃจ์ด์ ธ ์์๊ฒ์ด๋ค. ์์ฑ์ ์ฃผ์
์ผ๋ก ์์กด์ฑ ์ฃผ์
์ด ๊ตฌํ๋ ์ ์ฝ๋์ Dependent ํด๋์ค์ฒ๋ผ ๋ง์ด๋ค. ์ด ๊ตฌ์กฐ๋ฅผ Unit Testํ๋ ค๋ฉด Dependent๊ฐ ์๊ตฌํ๋ ์๋์ Dependency๋ฅผ ์ฌ์ฉํ์ ๋ ์๋๋๋ ๊ฒฐ๊ณผ์ ํ
์คํธ์ฉ Dependency๋ฅผ ์ฌ์ฉํ ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํ์ฌ, Denpendent ๊ฒ์ฆํ๋ค. ์ด๋ ์ด ๋ชจ์ ์ข
์์ฑ์ ์ ๊ณตํ๋ ํ
์คํธ์ฉ Dependency๋ฅผ Test doubles์ด๋ผ ๋ถ๋ฅธ๋ค (์ฐธ๊ณ ๋ก, ์ ์ฝ๋์์ ์ฌ์ฉํ Test double์ ์ข
๋ฅ๋ #3-3์ ์๋ Stub์ด๋ค).
#2 Test doubles์ ์ข ๋ฅ
#2-1 ๊ฐ์
class Math(private val mathCalculator: Calculation) {
fun doMathOperation(radius: Double): Double {
return mathCalculator.circleArea(5.0)
}
}
open class Calculation {
companion object {
const val PI = 3.141592
}
open fun circleArea(radius: Double): Double {
println("๋ก๊ทธ: circleArea(${radius}) ์ํ")
return piMultiply(square(radius))
}
open fun square(operand: Double): Double {
println("๋ก๊ทธ: ${operand}์ ์ ๊ณฑ ์ฐ์ฐ ์ํ")
return operand * operand
}
open fun piMultiply(operand: Double): Double {
println("๋ก๊ทธ: ${operand} ๊ณฑํ๊ธฐ ํ์ด ์ฐ์ฐ ์ํ")
return operand * PI
}
}
ํ๋ก์ ํธ์ ์ด๋ฌํ ๊ตฌ์กฐ๊ฐ ์๋ค๊ณ ์น์. ๋ด๊ฐ Unit testํ ํด๋์ค๋ Dependent์ธ Math ํด๋์ค๋ค. ๋ฌผ๋ก ์์ ์์์์๋ ๊ฒ์ฆํ ๊ฒ๋ ์๋ ์ด๋ผํ ํด๋์ค์ง๋ง, ์ค์ ํ๋ก์ ํธ์์๋ ๋งค์ฐ ๋ฐฉ๋ํ ํฌ๊ธฐ์ ํด๋์ค์ผ ๊ฒ์ด๋ค. #2-2 ~ #2-4์์ ์ด ํด๋์ค์ ๋ํ Test Double์ ์ข
๋ฅ๋ณ๋ก ๋ง๋ค์ด ํ
์คํธ๋ฅผ ์งํํด ๋ณด๊ฒ ๋ค.
#2-2 Fake (์ค์ ๊ตฌํ์ ๋จ์ํ)
// Fake ๊ฐ์ฒด๋ฅผ ์ด์ฉํ Unit test
fun main() {
// Dependency (Test double) ์ธ์คํด์ค ์ ์ธ
val testDouble = FakeCalculation()
// Dependent ์ธ์คํด์ค ์ ์ธ
val dependent = Math(testDouble)
// (radius๊ฐ์ผ๋ก 5.0์ด ๋ค์ด๊ฐ๋ค๊ณ ๊ฐ์ ํ) ๊ฐ์ด ๊ทธ๋ญ์ ๋ญํ ๊ทผ์ฌ๊ฐ์ผ๋ก๋ผ๋ ์๋ฌ์์ด ์ถ๋ ฅ๋๋์ง๋ง ํ์ธ
val result = dependent.doMathOperation(5.0)
println("Fake ์ถ๋ ฅ ๊ฒฐ๊ณผ ํ์ธ: ${result}")
}
class FakeCalculation : Calculation() {
override fun circleArea(radius: Double): Double {
return 5 * 5 * 3.0
}
}
์ฃผ๋ก Dependency์ ๋ํ ๊ฒ์ฆ์ด ๋ฑํ ํ์์์ ๋ ์ด๋ค. ํ์ํ ๊ฒ์ ๊ทธ๋ญ์ ๋ญ ๋์ค๋๊ธฐ๋ง ํ๋ฉด ๋๋ ์ถ๋ ฅ๊ฐ์ด๋ค. ์๋ ํด๋์ค์ธ Calculation์์ circleArea()ํจ์๊ฐ square() ๋ฐ piMultiply() ํจ์๋ฅผ ํธ์ถํ๋ ๋ฐ ์ด ์ ์ฐจ๋ ์งํค์ง ์๋๋ค. ์ ์ฐจ๋ฟ๋ง ์๋๋ผ ์ฌ์ง์ด ์ถ๋ ฅ๊ฐ์กฐ์ฐจ ์ ํํ ํ์๊ฐ ์๋ค. Fake Denpendency๋ก๋ถํฐ ์ ๋นํ ๊ทผ์ฌํ ์ถ๋ ฅ๊ฐ์ด ๋์จ๋ค๋ ๊ฒ๋ง ํ์ธํ๊ณ , Dependent์ ๊ฒ์ฆ์ ์ง์คํ๋ฉด ๋๋ค.
์๋ฅผ ๋ค์ด, ์ด๋ค Repository๋ฅผ ์ข
์์ฑ์ผ๋ก ๊ฐ์ง๋ ViewModel์ด ์๋ค๊ณ ํด๋ณด์. ๊ทธ๋ฆฌ๊ณ ํด๋น ํ๋ก์ ํธ๋ฅผ ์ง ํ๋ก๊ทธ๋๋จธ๋ ViewModel์ ์์ฃผ๋ก Unit testํ๋ ค๊ณ ํ๋ค. ์ด ๋, Repository๋ฅผ Fakeํ์์ Test double๋ก ๋๋งํ๋ค. Fake Repository์์๋ ์๋ Repository์์์๋ ๋ฌ๋ฆฌ Room์ด๋ Retrofit์ ๊ฑฐ์น๋ค๋ ์ ์ฐจ๋ ๋ฐ๋ฅด์ง ์๊ณ , ํด๋น ํด๋์ค์ ๋ฉ์๋๊ฐ ๋ฐํํ๋ ๊ฐ์ฒด๋ ๋์ถฉ ํ๋ ์ฝ๋ฉํด๋ฒ๋ฆฐ๋ค. ์ด์ฐจํผ ๋ชฉ์ ์ Dependent์ธ ViewModel์ ๊ฒ์ฆ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
#2-3 Stub (์ ํํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ Fake)
// Stub ๊ฐ์ฒด๋ฅผ ์ด์ฉํ Unit test
fun main() {
// Dependency (Test double) ์ธ์คํด์ค ์ ์ธ
val testDouble = StubCalculation()
// Dependent ์ธ์คํด์ค ์ ์ธ
val dependent = Math(testDouble)
// (radius๊ฐ์ผ๋ก 5.0์ด ๋ค์ด๊ฐ๋ค๊ณ ๊ฐ์ ํ) Unit test ์งํ
if (dependent.doMathOperation(5.0) == 78.5398) {
println("ํ
์คํธ ์ฑ๊ณต")
} else {
println("ํ
์คํธ ์คํจ")
}
}
class StubCalculation : Calculation() {
override fun circleArea(radius: Double): Double {
return 5 * 5 * 3.141592
}
}
Fake๋ ์ถ๋ ฅ ๊ทธ ์์ฒด์ ์๋ฏธ๋ฅผ ๋๋ค๋ฉด, Stub๋ ์ถ๋ ฅ๊ฐ๋ ๊ฒ์ฆ ๋์์ผ๋ก ๋ณธ๋ค. ์ฆ, ์ถ๋ ฅ๊ฐ์ด ์ ํํด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ์ ์ฝ๋์ StubCalculation()์ #3-2์ FakeCalculation๊ณผ๋ ๋ฌ๋ฆฌ ํ์ด๊ฐ์ ๋ถ๋ชจ ํด๋์ค์ธ Calculation()์ ๋์ผํ ๊ฐ์ผ๋ก ๋๊ณ ์๋ค. ๋ฌผ๋ก , radius๊ฐ์ ์ฌ์ ํ ํ๋ ์ฝ๋ฉ๋์ด์์ง๋ง ๋ง์ด๋ค. Stub์์ ์ค์ํ ๊ฒ์ Input์ผ๋ก ๋ญ๊ฐ๋ฅผ ๋ฃ์์ ๋, ๊ทธ Output์ด Original ํด๋์ค์ Output๊ณผ ๋์ผํด์ผ ํ๋ค๋ ์ ์ด๋ค.
#2-4 Mock (์ํธ์์ฉ์ ๊ฒ์ฆํ๋ Stub)
// Mock ๊ฐ์ฒด๋ฅผ ์ด์ฉํ Unit test
fun main() {
// Dependency (Test double) ์ธ์คํด์ค ์ ์ธ
val testDouble = MockCalculation()
// Dependent ์ธ์คํด์ค ์ ์ธ
val dependent = Math(testDouble)
// (radius๊ฐ์ผ๋ก 5.0์ด ๋ค์ด๊ฐ๋ค๊ณ ๊ฐ์ ํ) Unit test ์งํ
if (dependent.doMathOperation(5.0) == 78.5398) {
println("์ถ๋ ฅ๊ฐ์ ์ ํํจ")
println("ํ์ ์์
ํ์: ๋ฉ์๋ ํธ์ถ ํ์, ์์, ๋ฐฉ์ ๋ฑ Original๊ณผ ๊ฐ์์ง ํ์ธ")
} else {
println("ํ
์คํธ ์คํจ")
}
}
class MockCalculation : Calculation() {
override fun circleArea(radius: Double): Double {
println("๋ก๊ทธ: circleArea(5.0) ์ํ")
return piMultiply(square(5.0))
}
override fun square(operand: Double): Double {
println("๋ก๊ทธ: 5.0์ ์ ๊ณฑ ์ฐ์ฐ ์ํ")
return 25.0
}
override fun piMultiply(operand: Double): Double {
println("๋ก๊ทธ: 25.0 ๊ณฑํ๊ธฐ ํ์ด ์ฐ์ฐ ์ํ")
return 78.5398
}
}
์ด์ ์ํธ์์ฉ๊น์ง ๊ฒ์ฆํ๋ค. Input์ ๊ฐ์ ์ฌ์ ํ ํ๋ ์ฝ๋ฉ๋์ง๋ง, ๊ทธ ๊ฐ์ด ์ฒ๋ฆฌ๋๋ ๋ฐฉ์(๋ฉ์๋ ํธ์ถ ํ์, ์์, ๋ฐฉ์ ๋ฑ)์ด Original๊ณผ ๋๊ฐ์์ผ ํ๋ค. Output๋ ๋น์ฐํ Original์ Output๊ณผ ๊ฐ์์ผ ํ๊ณ ๋ง์ด๋ค. ํ์ง๋ง, ๋ชจ๋ Mock์ ์์ ์๋ ์ฝ๋์ฒ๋ผ ์ฌ์ฉ์ ์ ์ ํด๋์ค๋ก ๋ง๋ค์ด ์ฌ์ฉํ๊ธด ์ฝ์ง ์๋ค. ํนํ ์๋๋ก์ด๋์์๋ ์๋๋ก์ด๋ ํ๋ ์์ํฌ๊น์ง ๊ณ ๋ ค๋ฅผ ํด์ผํ๊ธฐ ๋๋ฌธ์ ์ฌ์ค์ ๋ถ๊ฐ๋ฅํ๋ค. ํ์ง๋ง ์คํ๋ ค ์ด ๋จ์ ์ ์ํด ์ ๋ง์ Mock ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์ถ๋ฒ๋์๋ค.
#3 ์์ฝ
Test double์ Dependent๋ฅผ ํ
์คํธํ๊ธฐ ์ํ ๋๋ฏธ Dependency๋ค.
#4 ์ ์ฒด ์์ค ์ฝ๋
Test double์ 3๊ฐ์ง ์ ํ
'๊นจ์ ๊ฐ๋ ๐ > ๊ธฐํ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ผ๋ก ๋จธ์คํฌ์ ์ฌ๊ณ ๋ฒ๊ณผ ์ถฉ๊ณ (0) | 2025.01.24 |
---|---|
Cold Flow์ Hot Flow (SharedFlow) (0) | 2024.08.07 |
์์กด์ฑ ์ฃผ์ (Dependency Injection) (0) | 2024.06.20 |
REST API (REpresentational State Transfer Application Programming Interface) (0) | 2024.05.26 |
API (Application Programming Interface) (0) | 2023.12.18 |