Spring | @DirtiesContext

yeonk·2023년 5월 21일
3

spring & spring boot

목록 보기
10/10
post-thumbnail

우아한테크코스 레벨2 지하철 노선도 미션을 하면서 테스트 격리를 위해 @DirtiesContext 를 사용했다.
미션 뼈대 코드에 사용된 어노테이션인데, 사용할 때 하더라도 사용법과 이유에 대해 알고 사용해야할 것 같아서 정리를 해보려고 한다.

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
	// ...
}





@DirtiesContext


스프링 테스트에서는 같은 Context를 사용하는 테스트가 존재할 때, 기존의 Context를 재활용한다(ContextCaching).
따라서 애플리케이션 컨텍스트의 구성이나 상태를 테스트 내에서 변경하지 않아야 한다.
그렇지 않다면 테스트간 격리가 수행되지 않을 수 있다(변경된 컨텍스트를 다른 테스트에서 사용하는 문제).
이 때, @DirtiesContext 가 해결책이 될 수 있다.
@DirtiesContext는 각 테스트를 수행할 때마다 context를 생성 가능하도록 한다.

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

테스트 컨텍스트는 해당 어노테이션이 붙은 테스트 클래스에는 애플리케이션 컨텍스트 공유를 허용하지 않는다.
이를 통해 테스트간 격리를 수행할 수 있다.





사용 방법


MethodMode

테스트 메서드에 @DirtiesContext로 애너테이션을 달 때 사용할 수 있는 모드이고, 아래와 같이 총 2가지이다.
기본값은 AFTER_METHOD 이다.

  • BEFORE_METHOD
@DirtiesContext(methodMode = BEFORE_METHOD)
@Test
public void test() {
 // context의 상태를 변경하는 코드
}
  • AFTER_METHOD (default)
@DirtiesContext
@Test
public void test() {
 // context의 상태를 변경하는 코드
}





ClassMode

테스트 클래스에 @DirtiesContext 어노테이션이 있을 때 사용할 수 있는 모드이고, 총 4가지 이다.
기본값은 AFTER_CLASS 이다.

  • BEFORE_CLASS

    • 클래스의 테스트가 시작하기 전에 새로운 Context를 재생성 한다.

    • 각 테스트 메서드들은 앞서 생성된 Context에서 실행된다.

@DirtiesContext(classMode = BEFORE_CLASS)
public class TestClass {
 // ...
}
  • BEFORE_EACH_TEST_METHOD

    • 클래스의 모든 테스트 메서드를 실행하기 전에 Context를 재생성한다.
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
public class TestClass {
 // ...
}
  • AFTER_EACH_TEST_METHOD

    • 클래스의 모든 테스트 메서드가 실행된 후에 Context를 재생성한다.
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
public class TestClass {
 // ...
}
  • AFTER_CLASS (default)

    • 클래스의 테스트가 모두 실행된 후에 새로운 Context를 재생성 한다.
@DirtiesContext
public class TestClass {
 // ...
}





단점


@DirtiesContext 를 사용하게 되면 테스트 격리는 가능하지만, 테스트를 할 때마다 스프링 컨텍스트가 매번 뜨게 되어 테스트가 느려질 수 있다(테스트 성능이 저하).

  • @DirtiesContext를 사용하여 지하철 미션에서 구현한 전체 테스트를 돌려봤을 때, 아래와 같은 결과를 확인할 수 있었다.





@Sql 적용

@Sql 애너테이션을 통해서 테스트 메서드 실행 전에 쿼리를 날려 데이터베이스를 초기화해줄수도 있다.
나의 경우 turncate 를 이용했다.

  • test/resources/truncate.sql
TRUNCATE TABLE STATION;
TRUNCATE TABLE LINE;
TRUNCATE TABLE SECTIONS;
@Sql(scripts = {"/truncate.sql"}, executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
	// ...
}





  • 기존에 @DirtiesContext를 사용하던 테스트를 위와 같이 수정한 결과이다.





@Sql을 사용할 때의 단점은 무엇일까?

앞서 살펴본 것 처럼 테스트 성능은 @DirtiesContext를 사용했을 때보다 @Sql을 사용했을 때가 더 좋았다. 하지만 @Sql의 단점은 없을까?

생각해보면 @Sql 을 사용한다면 테이블이 추가될 때마다 쿼리를 추가해주어야 한다. 이는 번거로울 수 있고, 개발자가 놓치기 쉬운 부분이라고 생각한다.

그렇기 때문에, 테이블의 변경이 적거나 크기가 작은 프로젝트에서는 @Sql을 사용해도 좋겠지만, 그렇지 않다면 이 단점까지 보완할 수 있는 방법을 모색하는 것이 좋을 것 같다.





정리


  • @DirtiesContext를 사용하면 테스트 마다 컨텍스트가 재생성되기 때문에 테스트간 격리를 수행할 수 있다.

  • @DirtiesContext의 옵션으로 classMode, MethodMode가 있고 필요에 따라 적용 범위도 선택할 수 있다.

  • @DirtiesContext 를 사용하면 테스트간 격리가 가능하지만, 테스트마다 컨텍스트가 재생성되기 때문 테스트 성능 저하를 초래할 수 있다.

  • @DirtiesContext의 테스트 성능 저하 문제를 해결하기 위해 @Sql 활용을 꾀할 수 있다.





참고 자료


Annotation Interface DirtiesContext
A Quick Guide to @DirtiesContext
Spring DirtiesContext
@DirtiesContext Example in Spring Test
인수테스트 성능 개선기 (feat.ContextCaching)
테스트 실행 시간 줄이기

0개의 댓글