Spring/스프링의 이해와 원리

[스프링의 이해와 원리] 2.2 테스트

JWonK 2023. 2. 4. 21:49
728x90
반응형

  스프링의 테스트 적용


지난 게시글에서 정리했듯이, JUnit은 독립적인 테스트를 지원하기 위해 매 번 새로운 애플리케이션 컨텍스트 오브젝트를 만들게 했다. 따라서 @Before 메소드가 테스트 메소드 개수만큼 반복되기 때문에 애플리케이션 컨텍스트도 그 개수만큼 생성된다.

설정도 간단하고 빈도 몇 개 없는 경우에는 별 문제가 되지 않지만, 빈이 많아지고 복잡해지면 애플리케이션 컨텍스트 생성에 적지 않은 시간이 걸릴 수 있다. 애플리케이션 컨텍스트가 만들어질 때는 모든 싱글톤 빈 프로젝트를 초기화한다.  어떤 빈은 오브젝트가 생성될 때 자체적인 초기화 작업을 진행해서 제법 많은 시간을 필요로 할 수 있다. 또, 애플리케이션 컨텍스트가 초기화될 때 어떤 빈은 독자적으로 많은 리소스를 할당하거나 독립적인 스레드를 띄우기도 한다. 

→ 이런 경우에는 테스트를 마칠 때마다 애플리케이션 컨텍스트 내의 빈이 할당된 리소스 등을 깔끔하게 정리해주지 않으면 다음 테스트에서 새로운 애플리케이션 컨텍스트가 만들어지면서 문제를 일으킬 수도 있다.

 

테스트는 가능한 한 독립적으로 매번 새로운 오브젝트를 만들어서 사용하는 것이 원칙이다. 

 

하지만 애플리케이션 컨텍스트처럼 생성에 많은 시간과 자원이 소모되는 경우에는 테스트 전체가 공유하는 오브젝트를 만들기도 한다. 따라서 애플리케이션 컨텍스트는 한 번만 만들고 여러 테스트가 공유해서 사용해도 된다.

 

문제는 JUnit이 매번 테스트 클래스의 오브젝트를 새로 만든다는 점이다. 따라서 여러 테스트가 함께 참조할 애플리케이션 컨텍스트를 오브젝트 레벨에 저장해두면 곤란하다. 그렇다면 스태틱 필드에 애플리케이션 컨텍스트를 저장해두면 어떨까?

 

JUnit은 테스트 클래스 전체에 걸쳐 딱 한 번만 실행되는 @BeforeClass 스태틱 메소드를 지원한다. 이 메소드에서 애플리케이션 컨텍스트를 만들어 스태틱 변수에 저장해두고 테스트 메소드에서 사용하게 할 수 있다.

→ JUnit5가 등장하게 되면서 @BeforeClass는 @BeforeAll로 변경되었다.

 

하지만 이보다는 스프링이 직접 제공하는 애플리케이션 컨텍스트 테스트 지원 기능을 사용하는 것이 더 편리하다.

 

 

 

 

 

 

 

  테스트를 위한 애플리케이션 컨텍스트 관리


스프링은 JUnit을 이용하는 테스트 컨텍스트 프레임워크를 제공한다. 테스트 컨텍스트의 지원을 받으면 간단한 애노테이션 설정만으로 테스트에서 필요로 하는 애플리케이션 컨텍스트를 만들어서 모든 테스트가 공유하게 할 수 있다.

 

 

 

 

 

 

 

▶ 스프링 테스트 컨텍스트 프레임워크 적용


테스트 클래스 레벨에 @Runwith와 @ContextConfiguration 애노테이션을 추가해준다.

→ JUnit5가 등장하게 되면서 @Runwith는 @ExtendWith로 변경되었다.

 

@ExtendWith

 단위 테스트에 공통적으로 사용할 확장 기능을 선언해주는 Annotation입니다. Spring의 기능을 확장해 테스트를 실행하기 위해 SpringExtension 클래스를 선언해 기능을 확장했습니다

 

@ContextConfiguration

테스트에 사용할 컨텍스트 설정을 담은 클래스 혹은 xml 파일을 선언해주는 Annotation입니다. 클래스 내 변수에 @Autowired가 있는데, 이러한 오브젝트에 주입할 스프링 빈을 생성하기 위해 선언해줍니다.

 

그리고 ApplicationContext 타입의 인스턴스 변수를 선언하고 스프링이 제공하는 @Autowired 애노테이션을 붙여준다. 

@ExtendWith({SpringExtension.class, MockitoExtension.class})
@ContextConfiguration(classes = {ApplicationConfig.class})
public class UserDaoTest{
    
    @Autowired
    private ApplicationContext context; // 테스트 오브젝트가 만들어지고 나면 스프링 테스트 컨텍스트에 의해 자동으로 값 주입
    
    @BeforeEach
 	public void setUp(){
    	...
    }
    
}

 

이렇게 해서 @Autowired를 이용하여 설정 파일에서의 ApplicationContext를 자동으로 주입 받으면서 하나의 테스트 클래스 내의 테스트 메소드는 같은 애플리케이션 컨텍스트를 공유해서 사용할 수 있다.

 

 

 

 

 

 

 

  테스트 클래스의 켄턱스트 공유


스프링 테스트 컨텍스트 프레임워크의 기능은 하나의 테스트 클래스 안에서 애플리케이션 컨텍스트를 공유해주는 것이 전부가 아니다. 여러 개의 테스트 클래스가 있는데 모두 같은 설정파일을 가진 애플리케이션 컨텍스트를 사용한다면, 스프링은 테스트 클래스 사이에서도 애플리케이션 컨텍스트를 공유하게 해준다.

 

따라서 수 백개의 테스트 클래스를 만들었는데 모두 같은 설정 파일을 사용한다고 해도 테스트 전체에 걸쳐 단 한 개의 애플리케이션 컨텍스트만 만들어져 사용된다.

 

 

 

 

 

 

  @DirtiesContext


이 애노테이션은 스프링의 테스트 컨텍스트 프레임워크에게 해당 클래스의 테스트에서 애플리케이션 컨텍스트의 상태를 변경한다는 것을 알려준다. 테스트 컨텍스트는 이 애노테이션이 붙은 테스트 클래스에는 애플리케이션 컨텍스트 공유를 허용하지 않는다. 

테스트 메소드를 수행하고 나면 매번 새로운 애플리케이션 컨텍스트를 만들어서 다음 테스트가 사용하게 해준다. 테스트 중에 변경한 컨텍스트가 뒤의 테스트에 영향을 주지 않게 하기 위해서다.

 

@DirtiesContext를 이용하면 일단 테스트에서 빈의 의존관계를 강제로 DI 하는 방법을 사용했을 때 문제는 피할 수 있다. 하지만 이 때문에 애플리케이션 컨텍스트를 매 번 만드는 건 조금 찜찜하다.

 

 

 

 

 

 

  테스트를 위한 별도의 DI 설정


테스트 만을 위한 새로운 설정 파일을 생성하여 DI 해주는 방법이다. 이제 테스트는 테스트 만을 위한 새로운 설정 파일 정보를 이용하여 애플리케이션 컨텍스트로를 사용할 수 있게 됐다. 나머지 테스트 코드는 수정할 필요 없이 설정 파일만 생성 및 수정해주면 된다.

 

번거롭게 하는 수동 DI 하는 코드나 @DirtiesContext도 필요 없다.

 

 

 

 

 

 

테스트 선택 방법


항상 스프링 컨테이너 없이 테스트할 수 있는 방법을 가장 우선적으로 고려한다. 테스트를 위해 필요한 오브젝트의 생성과 초기화가 단순하다면 이 방법을 가장 먼저 고려한다.

 

여러 오브젝트와 복잡한 의존관계를 갖고 있는 오브젝트를 테스트해야 할 경우 스프링의 설정을 이용한 DI 방식의 테스트를 이용한다. 테스트에서 애플리케이션 컨텍스트를 사용하는 경우에는 테스트 전용 설정파일을 따로 만들어 사용하는 편이 좋다.

 

테스트 설정을 따로 만들었다고 하더라도 때로는 예외적인 의존관계를 강제로 구성해서 테스트 해야할 경우도 있다. 이 때는 컨텍스트에서 DI 받은 오브젝트에 다시 테스트 코드로 수동 DI 해서 테스트 하는 방법을 사용한다.

테스트 메소드나 클래스에 @DirtiesContext 애노테이션을 붙인다.

 

 

 

 

 

 

 

 

▶ 정리


  • 테스트는 자동화돼야 하고, 빠르게 실행할 수 있어야 한다.
  • main() 테스트 대신 JUnit 프레임워크를 이용한 테슽 작성이 편리하다.
  • 테스트 결과는 일관성이 있어야 한다. 코드의 변경 없이 환경이나 테스트 실행 순서에 따라서 결과가 달라지면 안된다.
  • 테스트는 포괄적으로 작성해야 한다. 충분한 검증을 하지 않는 테스트는 없는 것보다 나쁠 수 있다.
  • 코드 작성과 테스트 수행의 간격이 짧을 수록 효과적이다.
  • 테스트하기 쉬운 코드가 좋은 코드다.
  • 테스트를 먼저 만들고 테스트를 성공시키는 코드를 만들어가는 테스트 주도 개발 방법도 유용하다. 
  • 테스트 코드도 애플리케이션 코드와 마찬가지로 적절한 리팩토링이 필요하다.
  • @BeforeEach, @AfterEach(JUnit4는 @Before, @After)를 사용해서 테스트 메소드들의 공통 준비 작업과 정리 작업을 처리할 수 있다.
  • 스프링 테스트 컨텍스트 프레임워크를 이용하면 테스트 성능을 향상시킬 수 있다.
  • 동일한 설정파일을 사용하는 테스트는 하나의 애플리케이션 컨텍스트를 공유한다.
  • @Autowired를 사용하면 컨텍스트의 빈을 테스트 오브젝트에 DI 할 수 있다.
  • 기술의 사용 방법을 익히고 이해를 돕기 위해 학습 테스트를 작성하자.
  • 오류가 발견될 경우 그에 대한 버그 테스트를 만들어두면 유용하다.
728x90
반응형