@WebMvcTest (feat.@EnableJpaAuditing)

SexyWoong·2024년 9월 26일
0

spring

목록 보기
10/11

문제

Controller를 테스트하기 위해 @WebMvcTest를 사용하였다. 하지만 'JPA metamodel must not be empty' 예외 문구가 발생하며 테스트에 실패하였다.

왜 이런 문제가 발생하는지 공부하며 알게 된 것들이 많아 정리해보려 한다.

@WebMvcTest가 무엇인가

Spring Boot에서 MVC 컨트롤러를 테스트하기 위해 사용되는 어노테이션이다.

  1. 웹 계층 집중 테스트
  • 컨트롤러와 관련된 웹 계층의 bean들만 로드한다. 이는 service, repository 등의 다른 계층 빈들을 로드하지 않기 때문에 테스트 속도가 빠르고, 웹 계층에 대한 집중적인 테스트가 가능하다.
  1. 특정 컨트롤러 지정 가능
  • @WebMvcTest(XXXController.class)와 같이 테스트하고자 하는 특정 컨트롤러를 지정할 수 있다. 이를 통해 필요한 컨트롤러만 로드하여 테스트의 효율성을 높일 수 있다.
  1. 의존성 Mocking
  • 컨트롤러가 의존하는 서비스나 리포지토리 빈들을 @MockBean 어노테이션을 사용하여 모의 객체로 주입할 수 있다.
  1. 빠른 테스트 실행
  • 전체 애플리케이션 컨텍스트를 로드하지 않기 때문에 테스트 실행이 빠르다.
  1. 슬라이스 테스트
  • @WebMvcTest는 애플리케이션의 웹 계층만 슬라이스하여 테스트하므로, 다른 계층의 영향을 받지 않고 독립적인 테스트가 가능하다.

만약 테스트하려는 XXXController가 XXXService를 의존할 경우 Mocking을 하지 않는다면?

테스트를 실행하면, 컨트롤러는 필요한 서비스 빈을 주입받지 못하게 되어 NoSuchBeanDefinitionException 또는 UnsatisfiedDependencyException과 같은 예외가 발생합니다. 이는 컨트롤러의 의존성이 충족되지 않아 애플리케이션 컨텍스트를 로드하는 데 실패하기 때문입니다.

해결방법

@MockBean을 통해 모의 객체를 주입한다.

JPA metamodel must not be empty

왜 발생했는가?

애플리케이션이나 테스트 설정에 @EnableJpaAuditing 어노테이션이 존재하면, JPA 감사 기능을 활성화한다. 이로 인해 jpaAuditingHandler 빈이 생성되는데, 이 빈은 JPA 메타모델에 의존한다.
하지만 @WebMvcTest 는 기본적으로 웹 계층의 빈들만 로드하기 때문에 JPA 엔티티나 레포지토리 등이 로드되지 않아 메타모델이 비어있게 되어 발생한다.

예외 메세지

Caused by: java.lang.IllegalArgumentException: JPA metamodel must not be empty

JPA 메타모델이 비어있으면 안 된다는 것을 의미한다. 즉, JPA 메타모델이 애플리케이션 컨텍스트에 로드되지 않았다는 뜻이다.

해결방법

여러가지가 있지만 다음과 같이 해결하였다.

package sample.cafekiosk.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
public class CafekioskApplication {

    public static void main(String[] args) {
        SpringApplication.run(CafekioskApplication.class, args);
    }

}

이 파일에서 @EnableJpaAuditing을 제거하고, 

package sample.cafekiosk.spring.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@Configuration
public class JpaAuditingConfig {

}

파일 따로 생성

왜 해결되었는가?

@EnableJpaAuditing 어노테이션을 메인 애플리케이션 클래스에서 별도의 설정 클래스로 이동함으로써 예외가 해결된 이유는 테스트 컨텍스트에서 JPA 감사(Auditing)기능의 자동 구성을 피할 수 있기 때문이다.

의문 1

@WebMvcTest를 통해 테스트를 한다면 웹 관련 빈들만 애플리케이션 컨텍스트에 등록하는 줄 알았는데, 왜 @EnableJpaAuditing 어노테이션이 동작하는지 이해를 못했다.

@EnableJpaAuditing의 역할

  • @EnableJpaAuditing 어노테이션은 Spring Data JPA에서 엔티티의 생성 및 수정 시간을 자동으로 관리하기 위한 감사 기능을 활성화한다.
  • 이 어노테이션은 AuditingHandler 및 관련 빈들을 애플리케이션 컨텍스트에 등록하며, JPA 메타모델을 필요로 한다.

@SpringBootApplication과 컴포넌트 스캔

  • @SpringBootApplication은 @ComponentScan을 포함하고 있어, 동일한 패키지와 그 하위 패키지에서 모든 컴포넌트(@Component, @Service, @Repository 등)를 스캔한다.
  • 메인 애플리케이션 클래스에 @EnableJpaAuditing이 있으면, 이 어노테이션도 컴포넌트 스캔의 대상이 되어 모든 컨텍스트에서 활성화된다.

@WebMvcTest의 동작 방식

  • @WebMvcTest는 웹 계층(컨트롤러)만을 테스트하기 위해 슬라이스된 애플리케이션 컨텍스트를 로드한다.
  • 기본적으로 컨트롤러와 관련된 빈들만 로드하고, 서비스, 리포지토리, JPA 엔티티 등은 로드하지 않는다.

문제의 원인이 되었던 어노테이션들을 각각 간략하게 정리해보았다. 그래도 왜 @EnableJpaAuditing 어노테이션이 활성되었는지 와닿지는 않았다. 다음의 사실을 알고나서 이해가 되었다.

몰랐던 부분

  • 테스트 시에도 CafekioskApplication 클래스는 애플리케이션 컨텍스트를 구성하는 데 사용된다.
  • 하지만 @WebMvcTest는 전체 애플리케이션 컨텍스트를 로드하는 것이 아니라, 메인 애플리케이션 클래스를 기반으로 필요한 빈들만 로드한다.
  • 즉, @SpringBootApplication의 설정을 참고하지만, 실제로 로드되는 빈들은 웹 계층 관련 빈들로 제한된다.
  • Spring Boot 테스트는 기본적으로 애플리케이션의 메인 설정 클래스를 사용하여 컨텍스트를 구성한다.

정리

@SpringBootTest시 에만 @SpringBootApplication을 통해 애플리케이션 컨텍스트를 설정한다고 생각했다. 하지만 @WebMvcTest시에도 메인 애플리케이션을 통해 기본적인 애플리케이션 컨텍스트를 설정하고, 웹 계층 관련 빈들만 등록한다는 사실을 알게되었다. 따라서 메인 애플리케이션을 설정함에 있어서 @EnableJpaAuditing이 있기에 JPA 감사기능이 활성화 되었던것이고, 별도의 파일을 통해 설정을 관리해서 감사기능이 활성화되지 않았고 그에 따른 에러가 발생하지 않은것이다.

profile
함께 있고 싶은 사람, 함께 일하고 싶은 개발자

0개의 댓글