WebMvcTest 어노테이션을 사용해서 컨트롤러를 테스트하는 코드를 작성하면서 발생한 문제에 대해 정리했다.
1. jpaAuditingHandler 빈 생성 에러
// 에러로그
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaAuditingHandler': Cannot resolve reference to bean 'jpaMappingContext' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: JPA metamodel must not be empty!
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: JPA metamodel must not be empty!
Caused by: java.lang.IllegalArgumentException: JPA metamodel must not be empty!
[첫 번째 방법: @AutoConfigurerDataJpa 어노테이션을 추가]
나는 auditing을 사용하고 있어서 @AutoConfigurerDataJpa 어노테이션을 추가하면 된다.
@AutoConfigurerDataJpa는 일반적인 Data JPA 테스트를 위한 Auto-configuration을 import한다.
[두 번째 방법: @DataJpaTest 어노테이션을 추가]
// 에러로그
Test ignored.
java.lang.IllegalStateException: Configuration error: found multiple declarations of @BootstrapWith for test class [com.membershipservice.controller.UserControllerTest]: [@org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper.class), @org.springframework.test.context.BootstrapWith(value=org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper.class)]
@DataJpaTest를 사용하는 방법
대부분의 테스트는 @DataJpaTest를 사용하는 것을 권장한다고 해서 했더니 위와 같은 에러가 뜬다.
@DataJpaTest는 JPA 컴포넌트에만 집중한다.
이 어노테이션을 사용하면 full auto-configuration이 안 된다.
JPA 테스트와 관련된 설정만 적용한다.
기본적으로 각 테스트의 마지막에 트랜잭션, roll back을 한다.
또한 내장된 in-memory database를 사용한다.
난 컨트롤러만 테스트하니까 JPA를 직접적으로 사용하지 않으므로 이 어노테이션이 필요없다.
2. Security 문제
WebMvcTest에서 Security를 사용하지 않고 싶었다.
@WebMvcTest(controllers = UserController.class,
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {JwtRequestFilter.class, WebSecurityConfigurerAdapter.class})
})
@AutoConfigureDataJpa
@AutoConfigureMockMvc(addFilters = false)
Filter도 스캔하니까 내가 작성한 JwtRequestFilter를 exclude했다.
addFilters = false로 Spring Security filters를 없앴다.
@AutoConfigureMockMvc 어노테이션은 MockMvc의 auto-configuration을 설정한다.
👇👇👇 아래는 시도했지만 실패했던 3가지 방법들
1. excludeFilters로 제외시킨다.
@WebMvcTest(controllers = UserController.class,
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {JwtRequestFilter.class})
})
@AutoConfigureDataJpa
이렇게 하면 메소드에 @WithMockUser 어노테이션을 붙여야 함
만약에 로그인 컨트롤러를 테스트한다고 했을 때 특히 더 이상하다.
2. 내가 작성한 SecurityConfig 클래스를 import해서 URL에서 따라서 인증을 적용할려고 함
@WebMvcTest(controllers = UserController.class,
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {JwtRequestFilter.class})
})
@AutoConfigureDataJpa
@Import(SecurityConfig.class)
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.membershipservice.conifg.SecurityConfig': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.membershipservice.conifg.CustomAuthenticationEntryPoint' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.membershipservice.conifg.SecurityConfig': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.membershipservice.conifg.CustomAuthenticationEntryPoint' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.membershipservice.conifg.CustomAuthenticationEntryPoint' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
테스트가 복잡해진다...
3. WebSecurityConfigurerAdapter 클래스를 exclude
@WebMvcTest(controllers = UserController.class,
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {JwtRequestFilter.class, WebSecurityConfigurerAdapter.class})
})
@AutoConfigureDataJpa
WebSecurityConfig 는 WebSecurityConfigurerAdater 를 상속하므로 WebSecurityConfgurerAdater 스캔을 제외하면 당연히 WebSecurityConfig 도 스캔되지 않는다.
출처
https://velog.io/@cieroyou/WebMvcTest%EC%99%80-Spring-Security-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0#-%EA%B2%B0%EB%A1%A0
이렇게 해도 security를 제거할 수 없다.
참고 👇
https://www.appsdeveloperblog.com/disable-spring-security-configuration-for-webmvctest/
https://www.appsloveworld.com/mongodb/100/19/using-embedded-mongodb-in-spring-junit-webmvctest