Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

[Spring 레퍼런스] 10장 테스트 #1

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.



10. 테스트

10.1 스프링 테스트 소개
테스팅은 엔터프라이즈 소프트웨어 개발에서 필수적이다. 이번 장에서는 유닛 테스트에 대한 IoC 원리의 부가가치와 통합 테스트의 이점에 집중한다. (엔터프라이즈에서 테스트를 전부 다루는 것은 이 레퍼런스 매뉴얼의 번위를 넘어선다.)


10.2 유닛 테스트
의존성 주입은 전통적인 자바 EE 개발이 하던 것보다 코드가 컨테이너에 덜 의존적이도록 한다. 어플리케이션을 구성하는 POJO는 스프링이나 다른 컨테이너없이 new 오퍼레이터를 사용해서 객체를 간단히 인스턴스화해서 JUnit과 TestNG로 테스트 할 수 있어야 한다. 코드를 독립적으로 테스트할 때 목(mock) 객체를(다른 가치있는 테스트 기법과 함께) 사용할 수 있다. 스프링의 아키텍쳐 권장사항을 따른다면 코드의 계층화(layering)와 컴포넌트화가 깔끔하게 될 것이고 훨씬 쉽게 유닛 테스트를 할 수 있다. 예를 들어 유닛 테스트를 하는 동안 퍼시스턴트 데이터에 접근하지 않고도 DAO나 레파지토리 인터페이스를 스텁(stubbing)하거나 모킹(mocking)해서 서비스 계층의 객체들을 테스트 할 수 있다.

실제 유닛테스트는 설정해야 할 런타임 인프라스트럭쳐가 없기 때문에 정말 빠르게 수행된다. 개발 방법론에서 유닛 테스트는 생성성은 아주 높혀줄 것이다. IoC 기반 어플리케이션에서 효율적인 유닛 테스트를 작성하는 데는 테스트 장의 이번 섹션은 별로 도움이 되지 않을 것이다. 하지만 유닛 테스트 시나리오들을 위해서 스프링 프레임워크는 다음의 목 객체와 테스트 지원 클래스를 제공한다.


10.2.1 목 객체

10.2.1.1 JNDI
org.springframework.mock.jndi 패키지는 테스트 슈트나 독립적인 어플리케이션의 간단한 JNDI 환경을 설정하는데 사용할 수 있는 JNDI SPI 구현체를 포함하고 있다. 예를 들어 JDBC DataSource가 자바 EE 컨테이너내에서 처럼 테스트 코드에서 간은 JNDI 이름들에 바인딩된다면 수정없이도 테스트 시나리오에서 어플리케이션 코드와 설정을 모두 재사용할 수 있다.


10.2.1.2 서블릿 API
org.springframework.mock.web 패키지는 웹 컨텍스트와 컨트롤러를 테스트하는데 유용한 스프링의 웹 MVC 프레임워크와 함께 사용할 수 있는 서블릿 API 목 객체의 광범위한 세트를 포함하고 있다. 이러한 목 객체들은 EasyMock같은 동적 목 객체들이나 MockObjects같은 이미 존재하는 서블릿 API 목 객체들보다 일반적으로 사용하기가 더 편하다.


10.2.1.3 포틀릿(Portlet) API
org.springframework.mock.web.portlet 패키지는 스프링의 포틀릿 MVC 프레임워크와 사용하도록 만들어진 포틀릿 API 목 객체들의 세트를 포함하고 있다.


10.2.2 유닛테스트를 지원하는 클래스

10.2.2.1 일반적인 유틸리티
org.springframework.test.util 패키지는 리플렉션기반의 유틸리티 메서드의 컬렉션인 ReflectionTestUtils를 포함하고 있다. 개발자들은 어플리케이션 코드를 테스트할 때 public이 아닌 필드를 설정하거나 public이 아닌 setter 메서드를 호출해야 하는 유닛 테스트나 통합 테스트 시나리오에서 이 메서드들을 사용한다. 다음과 같은 예시들이 있다.

  • 도메인 엔티티에서 프로퍼티에 대한 public setter 메서드와는 반대로 private 필드나 protected 필드에 접근하는 JPA나 하이버네이트같은 ORM 프레임워크.
  • private필드, protected 필드, setter 메서드, 설정 메서드에 의존성을 주입하는 @Autowired, @Inject, @Resource,같은 어노테이션의 스프링 지원


10.2.2.2 스프링 MVC
org.springframework.test.web 패키지는 ModelAndViewAssert를 포함하고 있다. ModelAndViewAssert는 Spring MVC ModelAndView 객체들을 다루는 유닛테스트에 JUnit과 TestNG나 다른 테스트 프레임워크와 함께 사용할 수 있다.

스프링 MVC 컨트롤러의 유닛 테스트
Spring MVC Controller를 테스트하려면 org.springframework.mock.web 패키지의 MockHttpServletRequest, MockHttpSession 등과 결합해서 ModelAndViewAssert를 사용해라.



10.3 통합 테스트

10.3.1 개요
어플리케이션 서버에 배포를 하거나 다른 엔터프라이즈 인프라에 연결하지 않고도 할 수 있는 통합테스트는 중요하다. 이러한 통합테스트는 다음을 테스트 할 수 있게 한다.

  • 스프링 IoC 컨테이너 컨텍스트의 제대로 된 연결
  • JBDC나 ORM 도구를 사용한 데이터 접근. 이는 SQL문, 하이버네이트 쿼리, JPA 엔티티 맵핑 등의 정확성 테스트를 포함한다.
스프링 프레임워크는 spring-test로 통합테스트를 훌륭하게 지원한다. 실제 JAR 파일의 이름에는 릴리즈 버전을 포함될 것이고 어디서 다운받는가에 따라(자세한 내용은 의존성 관리 부분을 참고해라.) 긴 형식의 org.springframework.test도 포함될 것이다. 이 라이브러리는 스프링 컨테이너의 통합테스트에 대한 중요한 클래스들은 담고 있는 org.springframework.test 패키지를 포함하고 있다. 이 테스트는 어플리케이션 서버나 다른 배포 환경에 의존하지 않는다. 이러한 테스트는 유닛 테스트보다는 느리지만 동등한 Cactus 테스트나 어플리케이션 서버에 배포해서 테스트 하는 원격 테스트보다는 훨씬 빠르다.

스프링 2.5부터는 유닛테스트와 통합테스트를 어노테이션 주도 Spring TestContext Framework의 형식으로 지원한다. TestContext 프레임워크는 사용하는 실제 테스트 프레임워크와는 관계없이 별도로 동작하므로 JUnit, TestNG 등을 포함한 다양한 환경에서 테스트수단을 제공한다.

JUnit 3.8 지원은 폐기됨
스프링 3.0부터 레거시 JUnit 3.8 기반의 클래스 계층(예시 AbstractDependencyInjectionSpringContextTests, AbstractTransactionalDataSourceSpringContextTests 등등)은 공식적으로 폐기되었고 차후 릴리즈버전에서는 제거될 것이다. 이 클래스들에 기반한 테스트 클래스들은 모두 Spring TestContext Framework로 마이그레이션해야 한다.

스프링 3.1부터는 Spring TestContext Framework의 JUnit 3.8 기반 클래스들 (예시 AbstractJUnit38SpringContextTests, AbstractTransactionalJUnit38SpringContextTests)과 @ExpectedException는 공식적으로 폐기되었고 차후 버전에서는 제거될 것이다. 여기에 기반하고 있는 테스트 클래스들은 모두 Spring TestContext Framework가 제공하는 JUnit 4나 TestNG 지원으로 마이그레이션해야 한다. 유사하게 @ExpectedException 어노테이션이 붙은 테스트 메서드는 JUnit과 TestNG에서 기대하는 예외에 대한 내장된 지원을 사용하도록 수정해야 한다.



10.3.2 통합테스트의 목표
스프링의 통합테스트 지원에는 다음의 주요 목표들이 있다.

  • 테스트 실행간에 Spring IoC 컨테이너 캐싱을 관리하기 위해서
  • 테스트 픽스처 인스턴스의 의존성 주입을 제공하기 위해서
  • 통합 테스트의 적절한 트랜잭션 관리를 제공하기 위해서
  • 통합테스트를 작성하는 개발자들을 지원하는 스프링기반 클래스를 제공하려고
다음의 섹션들은 각 목표들을 설명하고 자세한 구현체와 설정에 대한 링크를 제공한다.


10.3.2.1 컨텍스트 관리와 캐싱
스프링 TestContext 프레임워크는 일관되게 스프링 ApplicationContext를 로딩하고 이러한 컨텍스트들을 캐싱한다. 구동시간이 이슈가 될 수 있으므로 로딩된 컨텍스트 캐싱기능은 중요하다. (스프링 자체의 부하가 아니라 스프링 컨테이너가 인스턴스화된 객체들을 인스턴스화하는데 걸리는 시간 때문이다.) 예를 들어 50 ~ 100개의 하이버네이트 매핑파일을 가진 프로젝트는 매핑파일들을 로딩하는데 10 ~ 20초가 걸릴 것이고 각 테스트 픽스처의 각 테스트를 실행하기 전에 비용을 초래해서 전체 테스트 실행을 더 느리게 해서 생산성이 줄어들 수 있다.

테스트 클래스들은 XML 설정 메타데이터의 리소스 위치(보통은 클래스패스)를 담고 있는 배열이나 어플리케이션을 설정하는데 사용하는 @Configuration 클래스를 담고 있는 배열을 제공할 수 있다. 이러한 경로나 클래스는 web.xml이나 다른 배포 설정파일에서 지정한 것과 같거나 비슷하다.

기본적으로 설정된 ApplicationContext가 일단 로드되면 각 테스트마다 재사용된다. 그러므로 설정비용은 (테스트슈트당) 딱 한번만 발생하고 이어진 테스트 실행은 훨씬 빨라진아. 여기서 테스트 슈트라는 말은 같은 JVM에서 실행되는 모든 테스트를 의미한다. (예를 들어 해당 프로젝트나 모듈을 빌드하는 Ant나 Maven으로 실행하는 모든 테스트) 빈도수는 적지만 어플리케이션 컨텍스트를 깨뜨리는 테스트나 재로딩이 필요한 경우(예를 들어 빈 정의나 어플리케이션 객체의 상태를 수정해서) 다음 테스트를 실행하기 전에 설정을 다시 로딩하고 어플리케이션을 다시 빌드하도록 TestContext 프레임워크를 설정할 수 있다.

TestContext 프레임워크의 컨텍스트 관리와 캐싱을 봐라.


10.3.2.2 테스트 픽스처의 의존성 주입
TestContext 프레임워크가 어플리케이션 컨텍스트를 로드했을 때 TestContext 프레임워크는 의존성 주입으로 테스트 클래스의 인스턴스를 선택적으로 설정할 수 있다. 이는 어플리케이션 컨텍스트에서 미리 설정된 빈을 사용하는 테스트 픽스처를 설정하는 편리한 메카니즘을 제공한다. 다양한 테스트 시나리오(예시. 스프링이 관리하는 객체 그래프, 트랜잭션 프록시, DataSource 등을 설정하기 위해서)에 걸쳐서 어플리케이션 컨텍스트를 재사용할 수 있다는 큰 장점이 있으므로 각 테스트케이스에 복잡한 테스트 픽스처를 중복해서 설정하지 말아라.

예제와 같이 Title 도메인 엔티티에 대한 데이터 접근 로직을 수행하는 HibernateTitleRepository 클래스를 가진 시나리오를 생각해 보자. 다음 영역을 테스트하는 통합 테스트를 작성한다고 해보자.

  • 스프링 설정: 기본적으로 HibernateTitleRepository 빈의 설정과 관련된 모든 것이 제대로 되었고 존재하는가?
  • 하이버네이트 매핑 파일 설정: 모두 제대로 매핑되었고 지연로딩 설정이 제위치에 있는가?
  • HibernateTitleRepository의 로직: 이 클래스의 설정된 인스턴스가 예상대로 동작하는가?
TestContext 프레임워크의 테스트 픽스처의 의존성 주입을 봐라.


10.3.2.3 트랜잭션 관리
실제 데이터베이스에 접근하는 테스트의 공통적인 이슈는 퍼시스턴스 스토어의 상태에 영향을 받는다는 것이다. 개발용 데이터베이스를 사용하더라도 상태가 바뀌면 테스트에 영향을 줄 것이다. 또한 퍼시스턴트 데이터를 추가하거나 수정하는 것 같은 많은 작업은 트랜잭션 밖에서 수행(또는 확인)할 수 없다.

TestContext 프레임워크는 이 문제를 해결한다. 기본적으로 TestContext 프레임워크는 각 테스트마다 트랜잭션을 만들고 롤백한다. 개발자들은 트랜잭션의 존재를 가정하고 그냥 코드를 작성한다. 테스트에서 프록시된 객체들을 트랜잭션으로 호출하면 설정된 트랜잭션 시맨틱에 따라 제대로 동작할 것이다. 추가적으로 테스트 메서드가 트랜잭션내에서 선택한 테이블의 내용을 지우면 트랜잭션은 기본적으로 롤백을 하고 데이터베이스는 테스트를 수행하기에 앞서 데이터베이스의 상태를 원래대로 돌릴 것이다. 트랜잭션 지원이 테스트의 어플리케이션 컨텍스트에서 정의된 PlatformTransactionManager 빈으로 테스트 클래스에 제공된다.

트랜잭션을 커밋하고 싶다면(일반적이지는 않지만 특정 테스트가 유지되거나 데이터베이스를 수정하기를 원하는 경우에 유용하다.) @TransactionConfiguration와 @Rollback 어노테이션으로 트랜잭션을 롤백하는 대신에 커밋하도록 TestContext 프레임워크에 지시할 수 있다.

TestContext 프레임워크의 트랜잭션 관리부분을 봐라.


10.3.2.4 통합테스트를 지원하는 클래스
스프링 TestContext 프레임워크는 통합테스트 작성을 쉽게 해주는 여러가지 abstract 지원 클래스들을 제공한다. 이 기반 테스트 클래스(base test classes)는 개발자가 접근할 수 있는 편리한 인스턴스 변수와 메서드뿐만 아니라 잘 정의된 테스트 프레임워크 훅(hook)을 제공한다.

명시적인 빈 검색을 수행하거나 컨텍스트의 상태를 전체적으로 테스트하는 ApplicationContext
디비를 조회 SQL문을 실행하는 SimpleJdbcTemplate. 이러한 쿼리를 데이터베이스와 관련된 어플리케이션 코드를 실행하기 이전과 이후에 데이터베이스의 상태를 확인하는데 사용할 수 있고 스프링이 어플리케이션과 같은 트랜잭션 범위에서 이러한 쿼리를 실행한다는 것을 보장해준다. ORM 도구를 함께 사용하는 경우에는 false positives를 피해야 한다.

추가적으로 프로젝트에 한정된 인스턴스 변수와 메서드를 가진 어플리케이션 범위의 커스텀 슈퍼클래스를 생성하고자 할 수 있다.

TestContext 프레임워크의 지원 클래스를 참고해라.


10.3.3 JDBC 테스트 지원
org.springframework.test.jdbc 패키지는 표준적인 데이터베이스 테스트 시나리오를 간결하게 하는 JDBC 관련 유티리티 함수의 컬렉션인 SimpleJdbcTestUtils를 포함하고 있다. AbstractTransactionalJUnit4SpringContextTests와 AbstractTransactionalTestNGSpringContextTests는 내부적으로 SimpleJdbcTestUtils에 위임하는 편리한 메서드를 제공한다.


10.3.4 어노테이션

10.3.4.1 스프링 테스트 어노테이션
스프링 프레임워크는 유닛테스트와 통합테스트에서 TestContext 프레임워크와 결합해서 사용할 수 있는 스프링에 특화된 어노테이션의 다음 셋을 제공한다. 기본적인 속성값, 속성 별칭 등 더 자세한 정보는 각각의 Javadoc를 참고해라.

  • @ContextConfiguration
    테스트 클래스에 대해 ApplicationContext를 어떻게 로딩하고 어떻게 설정하는지 결정하는 클래스수준의 메타에디터를 정의해라. 특히 @ContextConfiguration는 컨텍스트를 로딩하는데 사용하는 ContextLoader 전략뿐만 아니라 어플리케이션 컨텍스트 리소스 locations나 @Configuration classes (하지만 둘 다는 아니다.)를 선언한다. 하지만 기본 로더가 리소스 locations나 설정 classes를 지원하기 때문에 보통은 명시적으로 로더를 설정할 필요가 없다.
    
    @ContextConfiguration(locations="example/test-context.xml", loader=CustomContextLoader.class)
    public class XmlApplicationContextTests {
      // class body...
    }
    

    
    @ContextConfiguration(classes=MyConfig.class)
    public class ConfigClassApplicationContextTests {
      // class body...
    }
    

    Note
    @ContextConfiguration는 기본적으로 수퍼클래스가 선언한 리소스 경로나 설정 클래스를 상속하도록 지원한다.

    더 자세한 내용과 예제는 컨텍스트 관리와 캐싱와 Javadoc을 참고해라.
  • @ActiveProfiles
    테스트 클래스에 대해서 ApplicationContext를 로딩할 때 어떤 빈 정의 프로파일이 활성화되어야 하는 지를 선언하려고 사용하는 클래스 수준의 어노테이션이다.
    
    @ContextConfiguration
    @ActiveProfiles("dev")
    public class DeveloperTests {
      // class body...
    }
    

    
    @ContextConfiguration
    @ActiveProfiles({"dev", "integration"})
    public class DeveloperIntegrationTests {
      // class body...
    }
    

    Note
    @ActiveProfiles는 기본적으로 수퍼클래스가 선언한 활성화된 빈 선언 프로파일을 상속하도록 지원한다.

    @ActiveProfiles의 더 자세한 내용과 예제는 환경 프로파일을 사용한 컨텍스트 설정와 Javadoc을 참고해라.
  • @DirtiesContext
    테스트 실행 중에 의존하는 스프링 ApplicationContext가 오염(dirtied)(예시. 어떤 방법으로 수정되거나 오류가 생김)되었고 테스트 통과여부와 상관없이 반드시 닫아야 한다는 것을 나타낸다. @DirtiesContext는 다음 시나리오에서 지원한다.

    • 현재 테스트 클래스 이후 클래스모드가 기본 클래스모드인 AFTER_CLASS로 설정된 클래스에 선언한 경우.
    • 현재 테스트 클래스에서 각 테스트 메서드 이후 클래스 모드가  AFTER_EACH_TEST_METHOD.로 설정된 클래스에 선언한 경우.
    • 현재 테스트 이후 메서드에 선언한 경우.
    테스트가 컨텍스트를 수정한 경우(예를 들어 빈 정의를 교체한 경우) 이 어노테이션을 사용해라. 이어지는 테스트들은 새로운 컨텍스트를 사용한다.

    JUnit 4.5+나 TestNG를 사용할 때는 같은 테스트 클래스내에서 클래스 수준 어노테이션과 메서드 수준 어노테이션으로 모두 @DirtiesContext 를 사용할 수 있다. 이러한 시나리오에서 전체 클래스 이후 뿐만 아니라 이러한 어노테이션이 붙은 메서드 이후에 ApplicationContext 를 dirty로 표시한다. ClassMode를 AFTER_EACH_TEST_METHOD로 설정했으면 클래스의 각 테스트 메서드 이후에 컨텍스트를 dirty로 표시한다.
    
    @DirtiesContext
    public class ContextDirtyingTests {
      // 스프링 컨테이너가 오염되는 테스트들
    }
    

    
    @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
    public class ContextDirtyingTests {
      // 스프링 컨테이너가 오염되는 테스트들
    }
    

    
    @DirtiesContext
    @Test
    public void testProcessWhichDirtiesAppCtx() {
      // 스프링 컨테이너가 오염되는 테스트들
    }
    

    어플리케이션 컨텍스트가 dirty로 표시되면 어플리케이션 컨텍스트는 테스트 프레임워크의 캐시에서 제거되고 종료된다. 그래서 같은 리소스 위치의 세트를 가진 컨텍스트를 필요로 하는 이어진 테스트를 위해서 의존하는 스프링 컨테이너를 재구성한다.
  • @TestExecutionListeners
    TestContextManager에 어떤 TestExecutionListener들이 등록되어야 하는지 설정하는 클래스수준의 메타데이터를 정의한다. 보통 @TestExecutionListeners는 @ContextConfiguration와 결합해서 사용한다.
    
    @ContextConfiguration
    @TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
    public class CustomTestExecutionListenerTests {
      // class body...
    }
    

    @TestExecutionListeners는 기본적으로 상속받은 리스너를 지원한다. 자세한 내용과 예제는 Javadoc를 참고해라.
  • @TransactionConfiguration
    트랜잭션이 적용된 테스트를 설정하는 클래스수준의 메타데이터를 정의한다. 특히, 원하는 PlatformTransactionManager의 빈 이름이 "transactionManager"가 아니라면 트랜잭션을 유도하는데 사용하는 PlatformTransactionManager의 빈 이름을 명시적으로 설정할 수 있다. 추가적으로 defaultRollback 플래그를 false로 변경할 수 있다. 보통 @TransactionConfiguration는 @ContextConfiguration와 결합해서 사용한다.
    
    @ContextConfiguration
    @TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
    public class CustomConfiguredTransactionalTests {
      // class body...
    }
    

    Note
    기본적인 관례로도 테스트 설정이 충분하다면 @TransactionConfiguration을 전혀 사용하지 않을 수 있다. 즉, 트랜잭션 매니저 빈의 이름이 "transactionManager"이고 트랜잭션을 자동으로 롤백하고자 한다면 테스트 클래스에 @TransactionConfiguration 어노테이션을 붙힐 필요가 없다.

  • @Rollback
    어노테이션이 붙은 메서드가 완료된 후에 트랜잭션을 롤백해야 하는지를 나타낸다. true인 경우 트랜잭션을 롤백하고 false이면 트랜잭션을 커밋한다. 클래스 수준에서 설정한 기본 롤백 플래그를 오버라이드하기 위해서 @Rollback를 사용해라.
    
    @Rollback(false)
    @Test
    public void testProcessWithoutRollback() {
      // ...
    }
    
  • @BeforeTransaction
    @Transactional 어노테이션을 통한 트랜잭션내에서 실행하려고 설정한 테스트 메서드에 트랜잭션을 시작하기 이전에 어노테이션이 붙은 public void 메서드를 실행해야 하는지를 나타낸다.
    
    @BeforeTransaction
    public void beforeTransaction() {
      // 트랜잭션이 시작되기 전에 실행해야 하는 로직
    }
    
  • @AfterTransaction
    @Transactional 어노테이션을 통한 트랜잭션내에서 실행하려고 설정한 테스트 메서드에 트랜잭션이 종료된 이후에 어노테이션이 붙은 public void 메서드를 실행해야 하는지를 나타낸다.
    
    @AfterTransaction
    public void afterTransaction() {
      // 트랜잭션이 종료된 이후에 실행해야 하는 로직
    }
    
  • @NotTransactional
    이 어노테이션이 붙은 테스트 메서드는 트랜젝션이 적용된 컨텍스트에서 실행하지 말아야 한다는 것을 나타낸다.
    
    @NotTransactional
    @Test
    public void testProcessWithoutTransaction() {
      // ...
    }
    
@NotTransactional는 폐기되었다
트랜잭션을 적용하지 않는 테스트 메서드는 별도의 (트랜잭션이 적용되지 않은) 테스트 클래스나 @BeforeTransaction, @AfterTransaction 메서드로 옮겨야 한다는 점에 지지해서 스프링 3.0부터는 @NotTransactional가 폐기되었다. 전체 클래스에 @Transactional 어노테이션을 붙히는 대신에 개별 메서드에 @Transactional 어노테이션을 붙히는 것을 고려해 봐라. 이렇게 하면 @NotTransactional을 사용할 필요가 없이 트랜잭션 메서드와 트랜잭션이 아닌 메서드를 같은 테스트 클래스내에서 섞어서 사용할 수 있다.



10.3.4.2 표준 어노테이션 지원
다음 어노테이션들은 스프링 TestContext 프레임워크의 모든 설정에서 표준적인 의미로 지원된다. 이 어노테이션들은 테스트에 한정되지 않고 스프링 프레임워크내 어디서나 사용할 수 있다.

  • @Autowired
  • @Qualifier
  • @Resource (javax.annotation) JSR-250을 제공했다면
  • @Inject (javax.inject) JSR-330을 제공했다면
  • @Named (javax.inject) JSR-330을 제공했다면
  • @PersistenceContext (javax.persistence) JPA를 제공했다면
  • @PersistenceUnit (javax.persistence) JPA를 제공했다면
  • @Required
  • @Transactional

10.3.4.3 스프링 JUnit 테스트 어노테이션
다음의 어노테이션들은 SpringJUnit4ClassRunner나 JUnit 지원클래스와 함께 사용할 때만 사용할 수 있다.

  • @IfProfileValue
    어노테이션이 붙은 테스트가 특정 테스트환경에서 사용가능하게 된다는 것을 나타낸다. ProfileValueSource가 제공된 name과 일치하는 value를 반환하면 테스트는 활성화된다. 이 어노테이션은 전체 클래스나 개별 메서드에 적용할 수 있다. 클래스수준의 적용하면 메서드 수준의 적용한 설정을 덮어쓴다.
    
    @IfProfileValue(name="java.vendor", value="Sun Microsystems Inc.")
    @Test
    public void testProcessWhichRunsOnlyOnSunJvm() {
      // Sun 마이크로시스템즈의 자바 VM에서만 동작하는 로직
    }
    

    대신에 JUnit 환경에서 TestNG형태의 테스트 그룹을 지원하기 위해서 values 목록(OR의 의미로)의 @IfProfileValue를 설정할 수 있다. 다음의 예제를 봐라.
    
    @IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"})
    @Test
    public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
      // 유닛테스트와 통합테스트 그룹에서만 동작하는 로직
    }
    
  • @ProfileValueSourceConfiguration
    @IfProfileValue 어노테이션으로 설정된 프로파일 값을 획득할 때 어떤 타입의 ProfileValueSource를 사용할 지를 지정하는 클래스수준의 어노테이션이다. @ProfileValueSourceConfiguration을 테스트에 선언하지 않았다면 기본값인 SystemProfileValueSource를 사용한다.
    
    @ProfileValueSourceConfiguration(CustomProfileValueSource.class)
    public class CustomProfileValueSourceTests {
      // class body...
    }
    
  • @Timed
    어노테이션이 붙은 테스트 메서드가 반드시 지정된 시간내에(밀리초단위) 실행이 완료되어야 한다는 것을 나타낸다. 테스트 실행시간이 지정된 시간을 넘으면 테스트는 실패한다.

    여기서 시간은 테스트 픽스처의 set up이나 tear down, 테스트의 반복(@Repeat 참고), 테스트 메서드 자체의 실행을 포함한다.
    
    @Timed(millis=1000)
    public void testProcessWithOneSecondTimeout() {
      // 실행에 1초이상이 걸리지 않는 로직
    }
    

    스프링의 @Timed 어노테이션은 JUnit의 @Test(timeout=...)와는 의미가 다르다. 특히 JUnit이 테스트 실행 타임아웃을 다루는 방법때문에 @Test(timeout=...)는 반복(repetitions)의 경우 각 반복(iteration)에 적용해서 테스트가 너무 오래 걸릴때 테스트가 실패하는 것은 막아준다. 반면 스프링의 @Timed의 시간은 테스트를 실행하는 전체 시간이고(모든 반복을 포함해서) 테스트가 실패하는 것을 막아주지 않고 실패전에 테스트가 종료하기를 기대한다.
  • @Repeat
    이 어노테이션이 붙은 메서드는 반드시 반복적으로 실행되어야 한다는 것을 나타낸다. 테스트 메서드가 실행되는 반복수는 어노테이션에 지정한다.

    반복하는 실행의 범위는 테스트 메서드 자체의 실행뿐만 아니라 테스트 픽스처의 set up이나 tear down도 포함한다.
    
    @Repeat(10)
    @Test
    public void testProcessRepeatedly() {
      // ...
    }
    

10.3.5 스프링 TestContext 프레임워크
스프링 TestContext 프레임워크(org.springframework.test.context 패키지에 있다.) JUnit이나 TestNG같은 사용하는 테스트 프레임워크에 상관없이 일반적이면서 어노테이션 주도의 유닛테스트와 통합테스트를 지원한다. TestContext 프레임워크는 어노테이션 기반 설정이 오버라이드할 수 있는 합당한 기본값으로 설정보다 관례(convention over configuration)의 중요성을 크게 적용했다.

일반적인 테스트 인프라스트럭처에 추가적으로 TestContext 프레임워크는 abstract 지원 클래스의 형태로 JUnit과 TextNG를 명시적으로 지원한다. JUnit의 경우에는 스프링이 POJO 테스트 클래스를 작성할 수 있도록 하는 JUnit Runner도 제공한다. POJO 테스트 클래스는 특정 클래스 계층을 확장(extend)해야할 필요가 없다.

다음 섹션에서는 TestContext 프레임워크의 내부를 살펴본다. TestContext 프레임워크의 사용방법에만 관심있고 커스텀 리스너나 커스텀 로더로 TestContext 프레임워크를 확장하는 방법에는 관심이 없다면 설정(컨텍스트 관리, 의존성 주입, 트랜잭션 관리) 지원 클래스와 어노테이션 지원 섹션으로 건너띄워도 무방하다.


10.3.5.1 핵심 추상화
TestContext 프레임워크의 코어는 TestContext, TestContextManager 클래스와 TestExecutionListener, ContextLoader, SmartContextLoader 인터페이스로 이루어져 있다. TestContextManager는 테스트마다 생성된다.(예시. JUnit에서 하나의 테스트 메서드의 실행마다) TestContextManager는 결국 현재 테스트의 컨텍스트를 담고 있는 TestContext를 관리한다. TestContextManager도 테스트가 의존성 주입 제공, 트랜잭션 관리 등으로 실제 테스트 실행하는 TestExecutionListener로 진행시키거나 위임하자마자 TestContext의 상태를 갱신한다. ContextLoader(또는 SmartContextLoader)는 해당 테스트를 위한 ApplicationContext를 로딩하는 책임을 진다. 다양한 구현체의 예제나 더 자세한 내용은 Javadoc과 스프링 테스트 슈트를 참고해라.

  • TestContext: 사용하는 실제 테스트 프레임워크와 무관하게 테스트가 실행되는 컨텍스트를 은닉화하고 컨텍스트 관리와 테스트 인스턴스에 대한 믿을만한 컨텍스트 관리와 캐싱지원을 제공한다. TestContext도 필요한 경우 ApplicationContext를 로드하는 ContextLoader(또는 SmartContextLoader)에 위임한다.
  • TestContextManager: 스프링 TestContext 프레임워크의 주요 진입점이다. 단일 TestContext와 잘 정의된 테스트 실행시 등록된 모든 TestExecutionListener의 신호(signal) 이벤트를 관리한다.

    • 특정 테스트 프레임워크의 before class methods 앞에
    • 테스트 인스턴스 준비
    • 특정 테스트 프레임워크 before methods 앞에
    • 특정 테스트 프레임워크 after methods 이후에
    • 특정 테스트 프레임워크 after class methods 이후에
  • TestExecutionListener: 리스너가 등록된 TestContextManager가 배포한 이벤트의 테스트 실행에 반응하는 listener API를 정의한다.

    스프링은 기본적으로 설정된 세가지 TestExecutionListener 구현체를 제공한다: DependencyInjectionTestExecutionListener, DirtiesContextTestExecutionListener, TransactionalTestExecutionListener. 각각 테스트 인스턴스의 의존성 주입, @DirtiesContext 어노테이션의 제어, 롤백이 기본인 트랜잭션을 적용한 테스트 실행을 지원한다.
  • ContextLoader: 스프링 TestContext 프레임워크가 관리하는 통합 테스트의 ApplicationContext를 로딩하는 Strategy 인터페이스는 스프링 2.5에서 도입되었다.

    스프링 3.1부터는 설정 클래스와 활성화된 빈 정의 프로파일을 지원하기 위해 이 인터페이스 대신 SmartContextLoader를 구현해라.
  • SmartContextLoader: 스프링 3.1에서 도입된 ContextLoader 인터페이스의 확장

    SmartContextLoader SPI는 스프링 2.5에서 도입된 ContextLoader SPI를 대체한다. 특히, SmartContextLoader는 리소스 locations나 설정 classes를 처리할 지를 선택할 수 있다. 게다가 SmartContextLoader는 로드한 컨텍스트에서 활성화된 빈 정의 프로파일을 설정할 수 있다.

    스프링은 다음의 독창적인(out-of-the-box) 구현체들을 제공한다.

    • DelegatingSmartContextLoader: 테스트 클래스에 대해 선언한 설정이나 기본 위치, 기본 설정 클래스의 존재에 따라 내부적으로 AnnotationConfigContextLoader나 GenericXmlContextLoader에 위임하는 기본 로더
    • AnnotationConfigContextLoader: @Configuration 클래스에서 어플리케이션 컨텍스트를 로딩한다.
    • GenericXmlContextLoader: XML 리소스 위치에서 어플리케이션 컨텍스트를 로딩한다.
    • GenericPropertiesContextLoader: 자바 프로퍼티 파일에서 어플리케이션 컨텍스트를 로딩한다.
다음 섹션에서는 어노테이션으로 TestContext 프레임워크를 어떻게 설정하는 지 설명하고 TestContext 프레임워크로 유닛테스트와 통합 테스트를 설정하는 방법의 예제를 볼 것이다.
2012/11/04 01:36 2012/11/04 01:36