private 함수 테스트 시 문제점
다음과 같은 양의 정수에 대해 곱셈을 실행하는 PositiveNumberMultiplier 클래스가 있다고 하자. 이 PositiveNumberMultiplier클래스는 정수를 가변인자로 받아 곱셈을 실행하는 multiplyAll 메서드를 가지고 있으며, multiplyAll메서드는 내부에서 가변인자로 들어온 모든 값이 양수인지 확인하는 isValid 함수를 가진다.
class PositiveNumberMultiplier() {
fun multiplyAll(vararg numbers: Int): Int {
if (isValid(*numbers)) {
throw IllegalArgumentException("인자는 양수여야 합니다")
}
return numbers.fold(1) { acc, number ->
acc * number
}
}
private fun isValid(vararg numbers: Int): Boolean {
return numbers.any { it <= 0 }
}
}
multiplyAll 함수에 대한 테스트를 실행하는 것은 문제가 없지만, isValid 함수는 private으로 선언되어 있어 테스트 시 접근성에 문제가 생긴다.
해결 방법1. 책임 분리하기
객체 내부에 private으로 선언된 함수가 있다는 뜻은, 객체가 자신의 책임이 아닌 역할까지 하고 있다는 것을 뜻한다. 따라서 이 함수를 테스트 하는 가장 좋은 방법은 책임을 분리하는 것이다. 따라서 다음과 같이 PositiveNumberValidator 객체가 해당 책임을 가져가도록 분리하면, 문제가 해결된다.
class PositiveNumberMultiplier(
private val positiveNumberValidator : PositiveNumberValidator
) {
fun multiplyAll(vararg numbers: Int): Int {
if (positiveNumberValidator.isValid(*numbers)) {
throw IllegalArgumentException("인자는 양수여야 합니다")
}
return numbers.fold(1) { acc, number ->
acc * number
}
}
}
class PositiveNumberValidator() {
fun isValid(vararg numbers: Int): Boolean {
return numbers.any { it <= 0 }
}
}
이렇게 책임을 분리하는 것이 가장 좋은 방법이지만, 종종 너무 많은 의존성으로 인해 이렇게 책임을 분리할 수 없는 상황이 있다. 이런 경우에는 다음 방법을 사용하면 된다.
해결방법2. private 함수를 호출하는 함수를 통한 테스트
불가피하게 private 함수에 대한 테스트를 해야 하면, private 함수를 호출하는 public 함수를 통해 해야 한다. 앞의 PositiveNumberMultiplier을 다시 살펴보자.
class PositiveNumberMultiplier() {
fun multiplyAll(vararg numbers: Int): Int {
if (isValid(*numbers)) {
throw IllegalArgumentException("인자는 양수여야 합니다")
}
return numbers.fold(1) { acc, number ->
acc * number
}
}
private fun isValid(vararg numbers: Int): Boolean {
return numbers.any { it <= 0 }
}
}
PositiveNumberMultiplier의 isValid는 multiplyAll에서 사용되고 있으므로, multiplyAll 함수를 통해 테스트를 진행해야 한다.
테스트는 다음과 같이 만들어질 수 있다. 음수가 multiplyAll에 들어갔을 때 애러가 throw 되는지 테스트 하면 된다.
class PositiveMultiplierTest {
private lateinit var positiveNumberMultiplier: PositiveNumberMultiplier
@BeforeEach
fun setUp() {
positiveNumberMultiplier = PositiveNumberMultiplier()
}
@Test
fun `test isValid works properly`() {
// Then
assertThrows(IllegalArgumentException::class.java) {
positiveNumberMultiplier.multiplyAll(-2, 3)
}
}
}
테스트를 실행해보면 다음과 같은 결과가 나온다.
정리
private 함수를 테스트하기 위해 private 함수의 책임을 분리하는 것이 가장 좋은 방법이지만, 만약 private 함수의 책임을 분리할 수 없다면 private 함수를 호출하는 public 함수를 호출함으로써 테스트 할 수 있다.