2. 테스트 - 학습 테스트로 배우는 스프링

이유석·2022년 5월 11일
0

Book - Toby's Spring

목록 보기
13/20
post-thumbnail

학습 테스트로 배우는 스프링

학습 테스트

  • 자신이 만들지 않은 프레임워크나 다른 개발팀에서 만들어서 제공한 라이브러리 등에 대해서 작성한 테스트

목적

  • 자신이 사용할 API나 프레임워크 기능의 테스트 코드를 작성해보면서 빠르고 정확하게 사용법을 익히는 것이다.

2.5.1 학습 테스트의 장점

다양한 조건에 따른 기능을 손쉽게 확인할 수 있다.

  • 자동화된 테스트 코드로 만들어지기 때문에 다양한 조건에 따라 기능이 어떻게 동작하는지 빠르게 확인할 수 있다.

학습 테스트 코드를 개발 중에 참고할 수 있다.

  • 다양한 기능, 조건 및 설정에 대한 테스트 코드를 개별적으로 만들고 남겨둘 수 있다.
    해당 테스트 코드를 실제 개발에서 샘플 코드로 참고할 수 있다.

  • 좀 더 나은 사용 방법을 발견했다면, 학습 테스트의 코드를 수정 후 다른 개발자와 공유할 수 있다.

프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다.

  • 기존에 사용중인 API나 기능의 업그레이드가 있을때, 자신의 제품에 적용하기 전 학습 테스트를 통하여 기능에 문제가 있는지 확인할 수 있다.

  • 버그가 있어 테스트가 실패하면 업그레이드 일정을 늦추거나, API 사용 방법의 변화에 맞춰 애플리케이션 코드를 수정할 계획을 세울 수 있다.

테스트 작성에 대한 좋은 훈련이 된다.

  • 프레임워크의 학습 테스트는 실제로 프레임워크를 사용하는 애플리케이션 코드의 테스트와 비슷하게 만들어진다.

  • 학습 테스트는 한두 가지 간단한 기능에만 초점을 맞추면 되기 때문에 테스트도 단순하다.

2.5.2 학습 테스트 예제

JUnit 테스트 오브젝트 테스트

JUnit은 테스트 메소드를 수행할 때마다 새로운 해당 클래스의 새로운 오브젝트를 만든다. 이를 테스트 해보자.

테스트 방법

  • 새로운 테스트 클래스를 만든다.
  • 해당 클래스에 서로 다른 테스트 메소드 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;
    }
}

sameInstance()는 실제로 같은 오브젝트인지를 비교한다.

위 방법에서 한가지 찜찜한 점이 있다.

  • 만약 첫번째 와 세번째 테스트 오브젝트가 같은 경우가 있다면 그것은 검증이 안된다.

세 개의 테스트 오브젝트 중 어떤 것도 중복이 되지 않는다는 것을 확인하도록 테스트 코드를 변경한다.

테스트 방법

  • 새로운 테스트 클래스를 만든다.
  • 해당 클래스에 서로 다른 테스트 메소드 3개를 만든다.
  • 테스트 오브젝트를 저장할 수 있는 컬렉션을 만들어둔다.
  • 매 테스트마다 현재 테스트 오브젝트가 컬렉션에 이미 등록되어 있는지 확인한다.
  • 없으면 자기 자신을 추가한다.
public class JUnitTest {

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

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

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

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

이런 학습 테스트를 이용하여

  • JUnit이 매번 새로운 테스트 오브젝트를 만든다는 사실을 분명히 확인할 수 있었다.
  • JUnit의 사용 방법을 익힐 수 있었다.
  • 여러 종류의 matcher를 사용해 볼 수 있었다.
    (is(), not(), sameInstance(), hasItem())

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

JUnit과 반대로 스프링의 테스트용 애플리케이션 컨텍스트는 테스트 개수와 상관없이 하나만 만들어진다.

이렇게 만들어진 테스트용 애플리케이션 컨텍스트는 모든 테스트에서 공유된다.

테스트에서 사용할 설정파일을 하나 만들어야 한다.
이 설정파일에서는 아무런 빈들 등록할 필요가 없다.

  • DI기능이 아닌 애플리케이션 컨텍스트가 만들어지는 방식을 확인하기 위한 것 이기 때문이다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

이전에 만들었던 JUnitTest 클래스에 @RunWith와 @ContextConfiguration 애노테이션을 추가한다.
그 후 방금 만들었던 설정 파일을 @ContextConfiguration의 locations 속성값으로 추가해준다.
@Autowired로 ApplicationContext를 주입받는다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/junit.xml")
public class JUnitTest {
	
    @Autowired
    ApplicationContext context;
    
	...
}

Autowired로 주입된 context 변수가 매 테스트에서 사용중인 오브젝트와 같은것인지 확인하는 코드를 추가한다.

테스트 방법

  • context를 저장해둘 스태틱 변수인 contextObject가 null인지 확인한다.
    • null이면
      • 첫번째 테스트로 간주하고 통과한다
      • 그 후 contextObject에 현재 context를 저장해둔다.
    • 2번째 테스트부터는 null이 아니다.
      • contextObject와 현재 context가 동일한지 확인한다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/junit.xml")
public class JUnitTest {

    @Autowired
    ApplicationContext context;

    static Set<JUnitTest> testObjects = new HashSet<JUnitTest>();
    static ApplicationContext contextObject = null;

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

        assertThat(contextObject == null || contextObject == this.context, is(true));
        contextObject = this.context;
    }

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

        // assertTrue 사용
        assertTrue(contextObject == null || contextObject == this.context);
        contextObject = this.context;
    }

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

        // either(), nullValue() 등 다양한 matcher 사용
        assertThat(contextObject,
                either(is(nullValue())).or(is(this.context)));
        contextObject = this.context;
    }
}

다양한 검증 방법을 연습해 볼 수 있도록, 각 테스트 메소드마다 다른 방법을 사용했다.

  • 코드는 다르지만 모두 같은 내용을 검증한다.

  • assertThat(T actual, Matcher matcher) 과 is() matcher
    매처와 비교할 대상인 actual에 boolean의 결과가 나오는 조건문을 넣는다.
    matcher에는 is(true)를 사용하여 해당 조거문이 true를 반환하는지 확인한다.

  • assertTrue(boolean condition) : 입력받은 조건문이 true를 반환하는지 확인한다.

  • either().or() : 두 개의 매처의 결과를 or 조건으로 비교하여 확인한다.
    즉 두가지 매처 중 하나만 true로 나와도 성공이다.

  • nullValue() : 오브젝트가 null인지 확인해준다.
    ex) assertThat(object, nullValue())

2.5.3 버그 테스트

정의

  • 코드에 오류가 있을 때, 그 오류를 가장 잘 드러내줄 수 있는 테스트

버그 테스트 작성 방법

  • 일단 해당 버그가 원인이 되어서 테스트가 실패하는 코드를 작성한다.

  • 그 후 버그 테스트가 성공할 수 있도록, 애플리케이션 코드를 수정한다.

  • 버스 테스트가 성공하면, 해당 버그는 잘 수정된 것 이다.

장점

  • 테스트의 완성도를 높여준다.
    기존 테스트에서는 미처 검증하지 못했던 부분의 테스트를 보완해준다.

  • 버그의 내용을 명확하게 분석하게 해준다.
    해당 버그를 테스트로 만들어서, 버그를 좀 더 효과적으로 분석할 수 있게 해준다.

  • 기술적인 문제를 해결하는데 도움이 된다.
    때로는 버그가 있다는 건 알겠지만, 해당 버그의 원인을 파악하기 힘들 때가 있다.
    이때 동일한 문제가 발행하도록 가장 단순한 테스트 코드를 작성하여 원인을 파악할 수 있다.

소스코드 : github

profile
https://github.com/yuseogi0218

0개의 댓글