[토비의 스프링 3.1] 2.5 학습 테스트로 배우는 스프링

2022. 7. 4. 23:54토비의 스터디

개발자는 자신이 개발하지 않은 프레임워크나 라이브러리를 사용하기 전에 테스트를 작성해보아야 한다.
이런 테스트를 학습 테스트라고 한다.

목적은 사용할 API나 프레임워크의 기능을 테스트를 통해 익히는 것이다. 이미 다른 사람들이 잘 만들어두었으므로 테스트의 주 목적은 검증과 거리가 멀다.

2.5.1 학습 테스트의 장점

  • 다양한 조건에 따른 기능을 손쉽게 확인할 수 있다.
    예제를 만들면서 학습할 수 있으며, 자동화된 테스트 코드를 통해 다양한 조건에 따라 기능이 어떻게 동작하는지 빠르게 확인할 수 있다.

  • 학습 테스트 코드를 개발 중에 참고할 수 있다.
    학습 테스트는 다양한 기능과 조건에 대한 테스트 코드를 개별적으로 만들고, 따로 남겨놓을 수 있다. 이렇게 저장한 코드는 실제 개발에서 샘플 코드로 참고하면 좋다. 아직 익숙하지 않은 기술을 사용할 때 미리 만들어진 테스트 코드는 좋은 참고 자료가 된다.

  • 프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다.
    요즘은 모든 제품의 업데이트가 빠르게 진행된다. 새로운 버전으로 업그레이드 할 때, 기존 버전과의 호환성 문제가 발생할 수 있다. 학습 테스트를 이용하면 새로운 버전의 프레임워크나 제품을 학습 테스트에만 먼저 적용해볼 수 있다.

  • 테스트 작성에 대한 좋은 훈련이 된다.
    당연하다. 학습테스트는 실제 프레임워크를 사용하는 애플리케이션 코드의 테스트와 비슷하게 작성된다.

  • 새로운 기술을 공부하는 과정이 즐거워진다.
    책, 레퍼런스를 읽기만 하는 공부는 쉽게 지루해진다. 테스트 코드를 만들면서 하는 학습은 흥미롭다. 테스트를 통과할때 나타나는 초록색 불은 개발자에게 일종의 유희..다

2.5.2 학습 테스트 예제

JUnit 테스트 오브젝트 테스트

지금까지 JUnit은 테스트 메소드를 수행할 때마다 새로운 오브젝트를 생성한다고 한다. 이것이 사실인지 이번 장에서 검증해본다. 

테스트 클래스를 만들고 테스트 메소드 3개를 만든다. 저 말이 사실이라면 오브젝트는 총 3번 생성될 것이다. 
테스트 클래스 자신의 타입으로 스태틱 변수를 하나 선언하고, 매 테스트 메소드에서 현재 스태틱 변수에 담긴 오브젝트와 자신을 비교한다.

비교 결과가 같지 않다면(새로운 오브젝트가 생성되었다면) 새로운 오브젝트를 생성하는 것이 사실이 된다.

public class JUnitTest {

    static JUnitTest testObject;

    @Test public void test1(){
        assertThat(this, is(not(sameInstance(testObject))));
        testObject = this;
    }

    @Test public void test2(){
        assertThat(this, is(not(sameInstance(testObject))));
        testObject = this;
    }

    @Test public void test3(){
        assertThat(this, is(not(sameInstance(testObject))));
        testObject = this;
    }
}

 테스트는 전부 성공한다. 즉, 메소드 실행마다 스태틱 변수에 저장한 오브젝트와 다른 새로운 테스트 오브젝트가 만들어졌다는 뜻이다. 

아래는 좀 더 테스트를 꼼꼼히 작성한 예시다. 세 개의 테스트 오브젝트 중 어떤 것도 중복이 되지 않는다는 것을 확인할 수 있다.

public class JUnitTest {

    static Set<JUnitTest> testObject = new HashSet<JUnitTest>();

    @Test
    public void test1(){
        assertThat(testObject,not(hasItem(this)));
        testObject.add(this);
    }

    @Test
    public void test2(){
        assertThat(testObject,not(hasItem(this)));
        testObject.add(this);
    }

    @Test
    public void test3(){
        assertThat(testObject,not(hasItem(this)));
        testObject.add(this);
    }

}

이 테스트도 역시 통과한다.

스프링 테스트 컨텍스트 테스트

이번에는 스프링 테스트 컨텍스트 프레임워크에 대한 학습 테스트를 작성한다. 
테스트용 애플리케이션 컨텍스트는 테스트 개수와 상관없이 딱 한개가 생성되며 모든 테스트에서 공유된다고 배웠다.
한번 확인해보자. 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/junit.xml")
public class JUnitTest {

    @Autowired // 매번 주입해주는 애플리케이션 컨텍스트는 항상 같은 오브젝트인지 확인해본다.
    ApplicationContext context;

    static ApplicationContext contextObject = null;

    @Test
    public void test1(){
        assertThat(contextObject == null || contextObject == this.context, is(true));
        contextObject = this.context;
    }


    @Test
    public void test2(){
        assertTrue(contextObject == null || contextObject == this.context);
        contextObject = this.context;
    }

    @Test
    public void test3(){
        assertThat(contextObject, either(is(nullValue())).or(is(this.context)));
        contextObject = this.context;
    }
}

테스트 메소드에서 확인할 사항은, "매번 동일한 애플리케이션 컨텍스트가 context 변수에 주입되었는지" 이다.

가장 처음에 null로 선언하였으므로, 테스트 메소드 중 가장 첫번째로 실행되는 메소드(메소드의 순서를 보장하지 않는다)에는 null값이 있을 것이다.

static ApplicationContext contextObject = null;

따라서 모든 메소드에서 검증하는 과정에 either 매처를 사용한다.

either() : 두 개 중 하나만 true면 테스트 성공

물론 테스트는 통과한다 ==> 테스트용 애플리케이션 컨텍스트는 테스트 개수와 상관없이 딱 한개가 생성된다.

2.5.3 버그 테스트

코드에 오류가 있는 경우, 그 오류를 가장 잘 드러내주는 테스트를 말한다. 버그 테스트의 특징은 실패하도록 만들어야 한다는 것이다. 버그가 원인이 되어 실패하는 코드를 만드는 것이다. 그리고 버그 테스트가 성공할 수 있도록 애플리케이션 코드를 수정한다. 이전 장에서 학습한 테스트 주도 개발(TDD)와 유사한 것 같다.

버그 테스트의 장점은 다음과 같다.

  • 테스트의 완성도를 높여준다.

  • 버그의 내용을 명확하게 분석할 수 있다.
    버그가 있을 때 그것을 테스트로 만들어 실패하게 하러면 문제가 발생한 이유를 명확하게 알아야 한다. 그 과정에서 버그로 인해 발생할 수 있는 다른 오류도 함께 발견할 수 있다.
     
  • 기술적인 문제를 해결하는 데 도움이 된다.
    버그의 존재는 알지만 원인을 알 수 없는 경우 동일한 문제가 발생하는 가장 단순한 코드와 그에 대한 버그테스트를 작성해보면 도움이 된다.

동등분할과 경계값 분석

  • 동등분할
    같은 결과를 내는 값의 범위를 구분해서 각 대표 값으로 테스트를 하는 방법이다. 예를 들어 어떤 작업의 결과가 true, false, 예외 발생 총 3가지라면 각 결과를 내는 입력 값이나 상황의 조합을 만들어 모든 경우에 대한 테스트를 진행하는 것이다.

  • 경계값 분석
    에러는 동등분할 범위의 경계에서 주로 발생한다는 특징을 이용해서 경계의 근처에 있는 값을 이용해 테스트하는 방법이다. 보통 숫자의 입력값인 0이나 그 주변 값 또는 정수의 최대, 최소값 등으로 테스트해보면 도움이 될 때가 많다. ==> 알고리즘 문제를 풀 때 반례를 찾고 싶은 경우 종종 도움을 받았다.