Test Double이란?
class A에 대한 테스트를 하고 싶은데 class B가 다른 class B와 의존성이 있을 경우 어떻게 해야할까? 이 경우 간단하게 생각하면 class B를 인스턴스화 하면 된다고 생각할 수 있다. 하지만 이런 경우 class B를 실제로 인스턴스화 하면 class B로 인해 class A의 테스트에 문제가 생길 수 있다. 또한 class A의 테스트가 class B에 의존성을 갖는 것 또한 옳은 테스트 방향이 아니다. 또한 어떤 경우에는 classB를 인스턴스화 하는 것이 불가능할 수도 있다.
이런 문제들을 해결하기 위한 것이 바로 Test Double이다. Test Double은 class B를 임의의 객체로 대체해서 class B의 동작을 모방해서 class A에서 사용할 수 있도록 만든다. 즉, class B의 실제 구현과는 상관 없이 실제로 필요한 동작만을 하도록 하는 것이다.
의존성이 있는 class를 테스트 할 때의 문제점
예를 들어 다음과 같이 class RandomStringGenerator가 있고 이 클래스는 interface RandomCharacterFactory에 의존성이 있다고 해보자.
class RandomStringGenerator(
private val characterFactory: RandomCharacterFactory
) {
fun generate(length: Int): String {
return StringBuilder().apply {
repeat(length) {
append(characterFactory.get())
}
}.toString()
}
}
interface RandomCharacterFactory {
fun get() : Char
}
RandomCharacterFactory에 대한 구현체는 다음과 같이 작성되어 있다.
class KoreanRandomCharacterFactory : RandomCharacterFactory {
override fun get(): Char {
return ('a'..'z').random()
}
}
뭔가 이상하지 않은가? KoreanRandomCharacterFactory인데 a부터 z 중 랜덤한 값을 반환하고 있다.
이상하지만 RandomStringGenerator에 대한 테스트를 만들어보자.
internal class RandomStringGeneratorTest {
lateinit var randomStringGenerator: RandomStringGenerator
@Before
fun setUp(){
randomStringGenerator = RandomStringGenerator(KoreanRandomCharacterFactory())
}
@Test
fun test() {
val result = randomStringGenerator.generate(5)
assertEquals(result.length, 5)
result.forEach {
assertTrue(('가'..'힣').contains(it))
}
}
}
이 코드는 실패한다.
하지만 이 테스트가 실패한 것은 RandomStringGenerator의 generate 메서드의 오류로 인해 생긴 것이 아니다. 바로 RandomStringGenerator에 의존성이 있는 class KoreanRandomCharacterFactory : RandomCharacterFactory 의 구현 오류로 인해 생긴 것이다.
즉, RandomStringGenerator의 테스트가 RandomStringGenerator의 구현이 아닌 의존성 있는 다른 클래스의 구현에 영향을 받는 것이다.
해결 방법
그러면 이 문제는 어떻게 해결해야 할까? 바로 RandomCharacterFactory의 구현체를 직접 만들어서 테스트 하는 것이다. 아래 코드에서는 object를 사용해 익명 객체를 만들었다.
internal class RandomStringGeneratorTest {
lateinit var randomStringGenerator: RandomStringGenerator
@Before
fun setUp(){
randomStringGenerator = RandomStringGenerator(object : RandomCharacterFactory {
override fun get(): Char {
return ('가'..'힣').random()
}
})
}
@Test
fun test() {
val result = randomStringGenerator.generate(5)
assertEquals(result.length, 5)
result.forEach {
assertTrue(('가'..'힣').contains(it))
}
}
}
이러한 테스트를 위해 가짜로 응답을 주는 객체를 Test Double이라 한다.
Test Double의 종류는 Fake, Stub, Mock 크게 세가지 이다. 다음 글에서는 이 각각에 대해 알아볼 것이다.
'Unit Testing' 카테고리의 다른 글
[Unit Testing] Mockito 사용해 Test Double 만들기 (0) | 2022.12.19 |
---|---|
Test Double이란 무엇인가? Test Double의 종류, 사용법 알아보기 (0) | 2022.12.18 |
Kotlin에서 사용할 수 있는 JUnit assert 종류 알아보기 : assertEquals, assertTrue, assertThrows, assertNotNull (0) | 2022.12.16 |
IntelliJ, Android Studio에서 Test Coverage 확인과 Test Coverage의 한계점 (0) | 2022.12.15 |
IntelliJ, Android Studio에서 Test 결과를 시각적으로 확인하는 방법 : Gradle 사용 (0) | 2022.12.14 |