6장 테스트 컨텍스트 프레임워크

토비의 스프링 2권 6장을 요약한 내용 입니다.

6.1 테스트 컨텍스트 프레임워크

  • 스프링은 테스트에 사용되는 애플리케이션 컨텍스트를 생성하고 관리하고 테스트에 적용해주는 기능을 가진 테스트 프레임워크를 제공한다. 이를 테스트 컨텍스트 프레임워크라고 부른다.

  • 자바에서 가장 많이 사용되는 테스트 프레임워크로는 JUnit과 TestNG가 있다.

테스트용 애플리케이션 컨텍스트 캐싱과 설정파일

  • Junit은 테스트 메소드를 실행할 때마다 매번 테스트 클래스의 새로운 오브젝트를 만든다. 따라서 모든 테스트는 서로 영향을 주지 않으며 독립적으로 실행됨을 보장한다.

  • 문제는 테스트가 독립적이라고 해서 매번 스프링 컨텍스트, 즉 컨테이너를 새로 만드는 건 매우 비효율적인 방법이다.

    • 하이버네이트와 같은 ORM은 초기에 엔티티에 대한 정보를 가져와 세션을 지원할 준비 작업을 하고 스레드를 생성하는 등의 많은 부가 작업을 필요로 한다.

  • 스프링은 테스트가 사용하는 컨텍스트를 캐싱해서 여러 테스트에서 하나의 컨텍스트를 공유할 수 있는 방법을 제공한다. (713p 그림 6-1 참고)

  • 테스트에 테스트 컨텍스트 프레임워크를 적용하려면 테스트 클래스에 두 가지 애노테이션을 부여해줘야 한다.

    • 먼저 @RunWith 애노테이션을 이용해서 JUnit 테스트를 실행하는 러너(Runner)를 스프링이 제공하는 것으로 변경해줘야 한다.

    • 컨텍스트의 설정파일을 지정하여 같은 테스트 클래스안의 테스트 메소드들은 하나의 설정파일로 만들어지는 애플리케이션 컨텍스트를 공유할수 있도록 한다. ( 설정파일 이름을 생략할 경우 현재 클래스 이름에 '-context.xml'이 붙은 파일이 디폴트 설정파일 이름으로 사용된다.

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration("/test-applicationContext")
      public class Test1 {
        @Test public void testMethod1() { ... }
        @Test public void testMethod2() { ... }
      }

      컨텍스트 설정의 상속과 컨텍스트 로더

    • 테스트 클래스를 구성할 때 필요하면 상속구조를 활용할 수도 있다.

    • 컨텍스트 파일 정보는 상속되어 서브클래스의 컨텍스트 파일 정보는 슈퍼클래스에서 정의된 것까지 포함된다.

      @ContextConfiguration("common-context.xml")
      public class SuperTest { ... }
      
      @ContextConfiguration("sub-context.xml") // Subtest의 설정 파일은 최종적으로 common-context.xml, sub-context.xml 두 개가 된다. 
      public class SubTest extends SuperTest { ... }

      공유 컨텍스트 사용 시 주의할 점

    • 캐싱 기법을 통해 하나의 컨텍스트를 여러 테스트가 공유할 수 있다는건 분명 테스트 컨텍스트 프레임워크의 장점이다.

    • 하지만 컨텍스트를 공유하는 테스트 메소드는 컨텍스트가 자신이 독점하는 것이 아니므로 그 구성이나 내부 정보를 함부로 변경해서는 안된다.

    • 그럼에도 어쩔 수 없이 컨텍스트의 빈 오브젝트를 조작하고 수정하는 작업이 꼭 필요한 테스트가 있을 수 도 있다.

      @Test
      @DirtiesContext
      public void test() { ... }
    • @DirtiesContext 애노테이션이 붙은 테스트가 수행되고 나면 스프링은 현 테스트 컨텍스트를 강제로 제거한다. (메소드 뿐만 아니라 클래스 레벨에 부여할 수도 있다.)

    • DAO 단독 테스트

    • DAO를 개발한 후에 서비스 계층을 거치지 않고 직접 DAO만 테스트해야 할 때가 있다. 문제는 JPA나 하이버네이트 등으로 만든 DAO는 트랜잭션이 시작되지 않은 채로 엔티티 메니저나 세션을 사용하면 예외가 발생한다는 점이다.

    • 그렇다고 DAO 다독 테스트를 하기 위해 서비스 계층을 매번 이용하거나 트랜잭션이 적용된 테스트용 서비스 계층 코드를 만드는 것도 매우 번거롭다.

      @Autowired JpaDao dao;
      
      @Test
      public void query() {
        List<User> users = dao.findUsers();
      }

      롤백 테스트

    • 롤백테스트란 테스트에서 진행되는 모든 DB 작업을 하나의 트랜잭션으로 묶어서 진행하고, 테스트를 마칠 때 츠랜잭션을 모두 롤백시키는 것이다.

    • 트랜잭션 매니저

    • 테스트에서는 TransactionTemplate 과 TransactionCalklback을 이용해 트랜잭션 경계를 설정한 후에 DB를 사용하는 빈을 호출해서 테스트를 진행한다. 테스트 메소드가 끝나고 나면 테스트에서 사용한 dao가 수정한 DB 데이터는 모두 테스트를 실행하기 이전 상태로 복구될 것이다.

    • @Autowird Jpa dao;
      
      @Test
      public void txTest() {
        new TransactionTemplate(transactionManager).execute(
            new TransactionCallback<Object>() {
                public Object doInTransaction(TransactionStatus status) {
                    status.setRollbackOnly();
      
                    dao.deleteAll();
                    dao.add(new Member(10, "Spring", 7.8));
                    assertThat(dao.count(), is(1));
      
                    return null; }});
      }

      @Transactional 테스트

    • 테스트의 @Transactional은 강제롤백 옵션이 설정된 트랜잭션으로 만들어진다는 점이다. TransactionStatus의 setRollbackOnly()가 호출되는 것과 동일한 방식으로 동작한다.

    • 트랜잭션이 시작되기 전이나 트랜잭션이 완전히 종료된 후에 해야할 작업이 있을 수도 있다. 이런 경우에는 스프링이 제공한 @beforetransaction과 @AfterTransaction이 붙은 메소드를 사용하면 된다.

      ORM 롤백 트랜잭션 테스트의 주의사항

    • ORM은 기본적으로 모든 작업 결과를 바로 DB에 반영하지 않는다. 대신 가능한 한 오랫동안 메모리에 변경사항을 저장하고 있다가 꼭 필요한 시점에서 DB에 반영한다.

    • 최적화를 위한 트랜잭션 내의 캐싱 기법이라고 볼수 있다.

    • ORM의 엔티티 오브젝트를 이용한 작업을 SQL로 만들어 DB로 보내는 작업을 플러스(flush)라고 한다.

Last updated