ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 테스트 코드 수행 시간 최적화하기
    백엔드/Spring 2023. 5. 8. 22:50
    반응형

     

    결론

    AS-IS:  40여개의 테스트 수행시간 - 13초 ~ 14초

    TO-BE: 80여개의 테스트 수행시간 - 2초

     

     

     

    1. @SpringBootTest, @WebMvcTest, @JdbcTest


    위 세가지는 테스트에서 사용할 수 있는 어노테이션입니다.

    각 어노테이션은 생성되는 빈의 수에 따른 차이가 존재합니다.

    @SpringBootTest는 실제 서버 실행시 필요한 빈을 모두 생성합니다.

    @WebMvcTest는 웹 환경에 필요한 빈만 생성합니다.

    @JdbcTest는 Jdbc 환경에 필요한 빈만 생성합니다.

     

    이렇기 때문에 테스트하는 목적에 따라 어노테이션을 잘 활용해서 적용할 수 있습니다.

    위 세가지 어노테이션 말고도 다양한 테스트 어노테이션이 있으니 잘 적용하면 시간을 살짝 단축시킬 수 있습니다.

     

    [예시]

    - 통합 테스트: @SpringBootTest

    - 컨트롤러 테스트: @WebMvcTest

    - DAO 테스트: @JdbcTest


     

     

     

    2. Context Caching 최대로 활용하기


    Spring TextContext Framework는 Context의 캐싱을 지원합니다.

    캐싱을 지원하는 이유는 컨텍스트를 생성할 때마다 시간이 걸리므로 전체적인 테스트 소요 시간이 길어지게 되기 때문에 조금이라도 단축시키려는 기능입니다.

    테스트 코드의 장점중에 하나가 개발자가 테스트 실행으로 인해 빠르게 피드백을 받을 수 있다는 것이 장점입니다. 하지만 컨텍스트를 새로 생성하느라 테스트 시간이 오래 걸린다면 테스트하는데 큰 결심을 해야겠죠..??

     

    이미 사용된 컨텍스트를 사용하기 위한 조건은 같은 조건의 테스트 환경이 제공되어야 합니다.

    @Autowired, @MockBean, ... 과 같은 것들이 모두 같아야 합니다.

    하나라도 바뀌면 서로 다른 환경에서 테스트를 하는 것으로 간주되어 새로운 Application Context를 생성하게 됩니다.

     

    만약 3개 종류의 테스트에서 Application Context를 생성한다면, 3개의 Application Context만을 만들어 재사용 할 수 있습니다.

    재사용할 수 있는 대표적인 방법은 추상 클래스를 만드는 것입니다.

     

    ex) 컨트롤러 테스트 추상 클래스

    @WebMvcTest({
            AdminController.class,
            HomeController.class
    })
    public abstract class ControllerTest {
    
        @MockBean
        protected ProductService productService;
    
        @MockBean
        protected MemberService memberService;
    
        @MockBean
        protected AuthenticationService authenticationService;
    
        @Autowired
        protected MockMvc mockMvc;
    }

     

    위 추상 클래스를 상속하면 컨트롤러 테스트를 수행하는 파일은 아래와 같이 작성할 수 있습니다.

    @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
    @SuppressWarnings("NonAsciiCharacters")
    class HomeControllerTest extends ControllerTest {
    
        @Test
        // 테스트 로직 코드 ...

     

    이 방법을 사용할 경우, 두 가지 단점이 존재합니다.

     

    1) 프로덕트 코드를 추가/수정할 때, 유지보수할 테스트 클래스가 더 늘어난다는 점

    2) 예시를 들어 특정 컨트롤러 테스트가 위의 ControllerTest에 있는 @MockBean에 있는 모든 객체를 몰라도 된다는 점

     

    하지만 추상 클래스로 관리하게 된다면 Application Context 캐싱을 관리하는 클래스를 명확하게 처리할 수 있기 때문에 단점보다 장점이 큰 방법이라고 생각합니다.


     

     

     

    3. @DirtiesContext는 꼭 필요할 때에만 사용하기


    @DirtiesContext는 Application Context가 오염이 되었을 때, ApplicaitonContext를 다시 생성하는 것입니다.

    여기서 오염이란 처음 등록된 빈이 변경되거나, 새로운 빈이 등록될 때를 말합니다. 만약 오염이 되지 않았으면 기존의 컨텍스트를 다시 재사용합니다.

     

    컨텍스트를 다시 새로 만드는 것이기 때문에 설정에 따라 클래스 실행 전후, 메서드 실행 전후로 @SpringBootTest, @WebMvcTest, @JdbcTest가 실행된다고 생각하면 됩니다.

     

    저의 경우엔 테스트 메서드로 인해 변경된 DB를 원래대로 복구하기 위해 @DirtiesContext를 사용해서 Application Context를 새로 생성했습니다.

    이건 단순히 데이터를 리셋하기 위해 @DirtiesContext를 사용하는 것은 원하는 것에 비해 비용이 큰 작업이기 때문에 다른 방법을 생각해야 합니다.

    그래서 @Sql을 어노테이션을 사용하여 데이터를 리셋하도록 변경했습니다.

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @AutoConfigureTestDatabase
    @Sql("/reset-cart_product-data.sql")
    public abstract class ApiControllerTest {

     

     

     

    4. IntelliJ에 적용된 자바 버전과 Gradle에 입력한 자바 버전 동일하게 만들기


    [이건 아직 공식 문서 내용을 못찾았습니다]

     

    Gradle에는 자바 버전 11이 적용되어 있는데, 제 IntelliJ에서는 자바 버전이 azul-13으로 되어 있었습니다.

    저는 테스트를 실행할 때, Gradle로 실행을 하는데, 먼저 Gradle 빌드를 한 뒤에 테스트 코드를 수행하는데, 빌드에 적힌 버전과 인텔리버전에 적힌 버전이 달라 여기서 시간이 걸리는 것 같습니다.

    그래서 IntelliJ 버전과 Gradle 버전을 똑같이 설정한다면 시간 단축을 크게 할 수 있습니다.


     

    반응형

    댓글

Designed by Tistory.