본문 바로가기
ETC

직접적인 테스트와 간접적인 테스트

by devson 2022. 5. 8.

협업 중에 생긴 일화를 바탕으로 직접적인 테스트와 간접적인 테스트에 대한 개인적인 소견에 대해 적어본다.

 


 

테스트 코드를 작성하다보면 코드에 대한 테스트가 중복적으로 발생할 여지가 생길 때가 있다.

이 때 테스트를 어디에 짜는 것이 좋을까?

 

말로만 하면 애매하니 간단한 예제를 들어보자.

 

우리가 쓰는 신용카드, 체크카드는 유효기간(년, 월)을 갖고 있다. 그리고 해당 유효기간과 동일한 년, 월 까지는 카드가 유효하다.

(e.g. 유효기간이 2022년 1월 이라면, 2022년 1월 31일 까지는 해당 카드는 유효하며, 2월 1일 부터는 만료된 것이다)

 

이 정보를 간단한 엔티티와 엔티티에 대한 정보를 담는 DTO 코드로 옮겨보자.

import java.time.LocalDateTime
import java.time.Month
import java.time.Year
import java.time.YearMonth

data class Card(
    val id: Long,
    val expirationYear: Year,
    val expirationMonth: Month,
) {
    fun isExpired(now: LocalDateTime): Boolean {
        return YearMonth.from(now) <= YearMonth.of(this.expirationYear.value, this.expirationMonth.value)
    }
}

 

import java.time.LocalDateTime

data class CardDto(
    val id: Long,
    val expirationYear: Int,
    val expirationMonth: Int,
    val expired: Boolean,
) {
    companion object {
        fun of(card: Card, now: LocalDateTime): CardDto =
            CardDto(
                id = card.id,
                expirationYear = card.expirationYear.value,
                expirationMonth = card.expirationMonth.value,
                expired = card.isExpired(now = now),
            )
    }
}

 

이제 이 코드에 대한 테스트를 작성한다고 해보자.

 

Card#isExpired에 대한 테스트와 CardDto#of에 대한 테스트를 짤 수 있을 텐데 두 메서드 모두 now: LocalDateTime라는 인자를 필요로 하며 이 인자에 따라 분기가 생겨 리턴하는 값이 변한다.

이 분기에 대한 테스트를 CardCardDto 모두 작성해야할까?그럴 순 있지만, 둘 중 한 곳에만 이러한 분기를 테스트하는 편이 아무래도 효율성이 좋을 것 같다.

 

"

그러면 CardDto#of 테스트에 이러한 분기를 테스트하면 CardDto#of 기능은 물론 Card#isExpired 메서드에 대한 기능도 간접적으로 커버가 될 것 같다.

그렇게하면 간단하게 CardDto#of 에 대해 시간에 따른 분기 테스트만 작성하면 적은 테스트로 많은 양의 코드를 커버할 수 있을 것이다.

"

 

라는 생각이 들 수 있을 것 같다. 물론 일리있는 말이라고 생각한다.

다만 개인적으로 만료 여부와 관련한 분기에 대한 테스트는 Card 테스트에 있어야한다고 생각한다.

그 이유는 크게 두 가지 인데

 

첫째로는, CardDto#of 메서드는 Card -> CardDto 매핑의 역할을 갖기 때문에 시간에 따른 분기에 대한 테스트는 CardDto에 대한 역할을 벗어나는 테스트라고 생각한다.

 

둘째로는, 테스트는 단순히 오류가 없음을 테스트하는 것 외에도 해당 기능에 대한 명세를 보여주는 역할도 한다.

그런 측면에서 해당 클래스에서 직접 테스트하지 않고 이를 사용하는 다른 클래스에서 간접적으로 테스트를 하는 것은 명세를 엉뚱한 곳에 두는 것이 아닐까라는 생각이 든다.

또한 테스트 코드를 작성하지 않은 사람이 그 간접적인 테스트 코드가 어디에 있는지도 알 수 없다.

 

 

라는 이유로 테스트는 어떤 통로를 통해 간접적으로 하기 보다는 가능하면 직접적으로 해당 기능을 테스트하는 것이 더 좋다고 생각한다.

 


 

당시에는 어렴풋이 간접적인 테스트 방식이 조금 부적절한 것 같다라는 생각이 들었지만 마땅한 논리적인 의견이 없어서 별다른 의견을 내지 않았지만,

다음에도 동일한 상황이 일어난다면 나의 의견에 대해서도 얘기를 나눠보면 좋을 것 같다 :)

(아마 또다른 재밌는 토론이 생기지 않을까?)

댓글