본문 바로가기

테스트

[테스트] private 접근 지정자에 대한 유닛 테스트

개요

안드로이드 개발 중 유닛 테스트(Unit Test)를 수행하던 중 private 접근 지정자로 지정된 메소드는 테스트 중 외부에서 접근이 불가능하기 때문에 이를 어떻게 해야 하나 고민하다 여러 블로그 및 포스팅을 참고하여 정리한 게시글이다.

 

TL;DR

private이 지정된 메소드 대부분은 굳이 테스트하지 않아도 되지만 일부에 복잡한 로직에 한해서는 작성할 수 있다.

 

Stack Overflow에 게시된 다양한 개발자들의 의견

이 private 메소드에 대한 테스팅은 유닛 테스트를 처음 시작하는 사람들이 처음 고민하는 관문인 것 같다. 마치 아래 포스팅과 같이.

아무래도 외부에서는 인스턴스의 private 함수/변수 등은 접근이 불가능하기 때문에 더 쉽게 만날 수 있는 고민이기도 하다.

 

이에 대한 사람들의 답변은 거의 고정되어 있다. "대부분의 경우에 private 메소드에 대한 테스트 코드 작성은 필요하지 않다."라는 의견이 지배적이다. 그 근거는 다음과 같다.

 

private이기 때문에 어느 외부 클라이언트에서도 이를 호출하여 특정 방식으로 작동하기를 기대하지 않는다. 반면에 public은 어느 외부 클라이언트가 호출하여 정해진대로 작동하길 원하기 때문에 테스트가 필요하다.

 

말이 조금 어렵지만 그 뜻은 간단하다. 다음 예제를 보자.

class Engine{

    public fun cooling() : Boolean{
    	return try{
            //쿨링 작업 수행
            //쿨링 팬 가동
            startCoolingPan()
            
            //...
            
            true
        }catch{
            false
        }
    }
    
    private fun startCoolingPan(){
    	//쿨링팬 가동
    }
}

class Car(
	private val engine: Engine
){
    fun cooling() : Boolean{
    	return engine.cooling()
    }
}

Car 클래스는 Engine 클래스를 주입받는다. 여기서 Engine에는 엔진의 열을 식혀주는 public fun cooling()과 쿨링팬을 동작시키는 private fun startCoolingPan()이 있다. Car에서는 Engine 클래스의 cooling() 메소드를 호출하여 예상하는 Boolean 결과를 받기를 기대한다. 하지만 startCoolingPan()을 외부 클래스인 Car에서 접근할 수 없고 특정 동작을 하기를 기대하지 않는다. 이게 private 메소드를 유닛 테스트하지 않는 이유이다.

 

 

너무 근거가 빈약한 이유가 아닌가?

위를 보고 이게 유닛 테스트를 하지 않는 이유라는 것은 너무 근거가 빈약한 게 아닌가 라는 생각이 든다. 왜냐면 테스트의 가장 중요한 목적 중 하나는 내가 작성한 코드가 올바르게 동작하는지 확인하는 것이기 때문이다. 실제 이와 같은 의견은 타당한 의견이다.

 

채택된 답변

먼저, 위 질문에 대한 답변이다. 위에서 설명한 듯 외부에서 특정 동작을 기대하지 않기 때문에 유닛 테스트는 굳이 필요 없다고 설명한다. 이 답변에 대한 댓글 중 다음을 읽어 볼 필요가 있다.

 

채택된 답변에 대한 댓글

이 개발자는 때에 따라 private 메소드를 테스트할 필요가 있다고 주장한다. 해당 메소드가 꽤나 복잡한 경우 테스트를 통해 정상적으로 동작하는지 보장할 필요가 있다는 것이다. 또한, 공유 데이터가 존재할 경우 이를 별도의 클래스로 분리하는게 쉽지 않다고 보충했다. 하지만, private 메소드가 매우 복잡하여 별도의 단위 테스트가 필요할 정도가 되었다면 이를 처리하는 자체 클래스가 필요하다는 의미라는 점도 그의 답변에서 확인할 수 있다.

 

위를 요약하면, X 클래스에 존재하는 private 메소드는 복잡성에 따라 유닛 테스트 여부를 결정한다는 것이다. 하지만, private 메소드가 복잡하다는 것은 다른 클래스로 분리될 필요가 있다는 것을 암시한다. 이렇게 X 클래스에서 Y 클래스를 새로 추출하여 설계한다면, Y 클래스의 public 메소드로 해당 작업이 리팩토링될 것이다. X에서 해당 하는 작업을 접근해야 하기 때문이다. 이러면 자연스럽게 public이 된 메소드를 유닛 테스를 할 수 있게 된다.

 

유닛 테스트 작성의 다양한 이유

테스트 코드를 작성하는 근본적인 이유는 코드가 원하는대로 동작하는지 확인하기 위함이다. 그렇기 때문에 아까와 같은 의문을 품을 수 있었다. 하지만 다음과 같은 이유들도 유닛 테스트를 작성하는 중요한 이유 중 하나이다.

  • 협업 중 개발자 A는 X클래스를 생성하고, 개발자 B는 Y클래스를 생성했다. X는 Y에 의존할 경우 X에서 접근하는 Y 클래스의 메소드들이 정상적으로 동작하는지 확인해야 한다. 이는 결국 Y의 public 메소드의 테스트 필요성을 의미한다. (이는 public 메소드만 테스트 하는게 좋다는 의견을 지지한다.)
  • 리팩토링 시 안정성을 확보할 수 있다.
  • 유닛 테스트를 통해 테스트 코드를 작성하다보면 자연스럽게 코드가 개선된다.

 

 

마무리

유닛 테스트도 개발자가 넘어야 할 산 중 하나인 것 같다. 시작할 때는 꽤나 어려움을 많이 느꼈지만, 계속해서 테스트 코드를 작성하다보니 금방 익숙해지는 것 같기도 하다.

 

참조