JUnit in Action : 6. 스텁을 활용한 포괄적인 테스트

    들어가기 전

    이 글은 JUnit in Action 6장을 공부하며 작성한 글입니다. 

     

    6. 스텁을 활용한 포괄적인 테스트

    어플리케이션을 개발하다보면 테스트 대상 코드가 다른 클래스에 종속되어 있을 때가 종종 있다. 그 클래스가 다른 클래스에 종속되어있고, 역시 또 다른 클래스에 종속되기도 한다. 이처럼 외부에 의존적인 어플리케이션이 있다면 테스트를 할 때 어려운 일이다.

    특정 런타임 환경에 의존적인 어플리케이션을 단위 테스트 하는 것은 어렵다. 테스트는 안정적이어야하고, 반복적으로 수행해도 매번 동일한 결과를 내야한다. 따라서 테스트를 올바로 수행하기 위해서는 환경을 제어할 수 있어야 한다. 

    동일한 환경을 갖춰서 테스트 용으로 사용하면 좋겠지만, 다른 회사에서 제공하는 서버라면 동일한 환경을 구축할 수 없다. 이런 경우에 선택할 수 있는 방법으로 더미 객체를 이용할 수 있다. 더미 객체를 이용하는 방법은 스텁(Stub)과 목(Mock)이 존재한다. 

    여기서는 스텁을 이용한 테스트를 확인해본다. 

     

    6.1 스텁이란?

    스텁은 실제 객체의 기능을 대체하는 더미 객체를 의미한다. 주로 의존하는 객체가 아직 군혀되지 않았거나, 특정 조건에서만 실행되어야 하는 경우에 사용된다. 예를 들어 파일을 읽는 기능을 하는 객체를 스텁으로 대체하여 파일 시스템에 접근하지 않고도 테스트를 수행할 수 있다. 

    스텁은 테스트 코드를 의존 객체로부터 격리시킬 목적으로 런타임에 실제 코드 대신 삽입되는 코드 조각이다. 복잡한 행위를 직접 구현하기 보다는 단순한 코드를 한 조각 넣어서 해결하는 방법이다. 

    스텁을 사용해서 테스트를 하면, 테스트 대상이 되는 객체를 수정할 필요가 없다. 따라서 테스트 대상 그 자체를 테스트 하기 때문에 테스트 객체는 런타임에서 사용될 객체와 동일하다고 봐도 된다. 따라서 테스트의 신뢰도는 높아진다. 

    스텁의 단점

    스텁 객체는 대체하려는 코드와 동일한 로직을 가지도록 만들어야 한다. 로직이 복잡할수록 구현해야하는게 어려울 수 있기 때문에 스텁 자체가 단점을 가질 수 있다. 

    • 제작이 복잡하여 스텁 자체를 디버깅하는 상황도 종종 발생한다.
    • 스텁이 복잡하기 때문에 유지보수가 어려워 질 수 있다. 
    • 상세한 테스트에는 적합하지 못하다.
    • 상황에 따라 다른 스텁 정책이 요구된다. 

    스텁은 파일 시스템, 서버와의 커넥션, DB등 외부 시스템 전체를 대체하는 용도로 주로 사용된다. 

     

    6.2  Http 커넥션을 스터빙하기

    WebClient 클래스가 있고, getContent() 메서드가 존재하고 있다. 이 때 getContent() 메서드가 정상적으로 동작하는지 단위 테스트를 하고 싶다고 해보자. 이곳에 보면 HttpConnection을 얻어와서 서버로부터 데이터를 얻어오는 작업을 하고 있다. 

    public class WebClient {
    
        public String getContent(URL url) {
            StringBuffer content = new StringBuffer();
            try{
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoInput(true);
                InputStream is = connection.getInputStream();
                byte[] buffer = new byte[2048];
                int count;
                while (-1 != (count = is.read(buffer))) {
                    content.append(new String(buffer, 0, count));
                }
            } catch (IOException e) {
                return null;
            }
            return content.toString();
        }
    }

    getContent()를 테스트 하기 위해서는 기본적으로 요청을 보냈을 때 응답을 해주는 웹서버가 필요하다. 그렇다면 웹 서버를 진짜로 사용한다면 무슨 단점이 있을까?

    웹서버를 직접 사용했을 때의 단점

    • 테스트 시작 전에 모든 환경이 잘 가동되고 있는지 확인해야함. 
    • 웹 페이지를 웹 서버에 배포하고, 웹 서버를 가동 시킨 후에야 테스트 수행이 가능하므로 테스트 실행의 자동화가 어려움. 

    웹 서브를 직접 사용해서 사용하게 되면 위와 같은 단점이 있다. 단위 테스트를 하는데 너무 복잡해진다. 이런 경우 스텁을 통해서 해결해 볼 수 있다. 

     

    그렇다면 어떤 대상을 스텁해야할까?

    여기서 스텁해 볼만한 대상은 두 가지다. 실제로는 더 있을 수도 있지만, 이 정도 상태로 처리해보고자 한다. 

    • 서버 Stub
    • HttpURLConnection을 Stub 

    서버 Stub

    서버를 Stub 한다고 하면, 핵심 로직뿐만 아니라 HttpUrlConnection 클래스를 통해 코드 외부에 존재하는 커넥션 부분까지 테스트를 한다. 즉, 단위 테스트와 동시에 통합 테스트도 일부 수행한다는 장점을 가진다. 이 경우, 서버 자체를 스텁 객체로 만들어서 좀 더 간단히 처리할 수도 있고 Jetty 같은 임베디드 서버를 띄워서 스터빙 할 수 있다. 

    특히 Jetty를 이용해서 서버를 스터빙 하는 경우, Jetty의 사용법까지 숙지해야하기 때문에 단위 테스트에 대한 생산성이 떨어질 가능성이 매우 높다. 좀 더 쉬운 방법으로는 Connection을 Stub하면 된다. 

    커넥션 Stub

    웹 서버의 리소스를 스텁으로 대체하는 대신 HTTP 커넥션을 스텁할 수 있다. 커넥션을 효과적으로 테스트 할 수는 없지만, 단위 테스트의 목적은 아니기 때문에 문제는 없다. 따라서 단위 테스트만 간단하게 아고 싶다면 커넥션을 Stub 하는 것이 더욱 효과적이다. 

    댓글

    Designed by JB FACTORY