본문 바로가기
Book/스프링 부트와 AWS 웹 서비스

JUnit 테스트에 Spring Security 적용하기

by 달의 조각 2023. 2. 22.
이 글은 이동욱 님의 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 책을 읽으며 정리한 글입니다.

 

  JPA 구성과 스프링 시큐리티를 적용한 후 이전에 작성했던 테스트들을 돌려 보면 아래와 같이 통과되지 않는다. @WebMvcTest의 특성을 정리하고 인증된 사용자를 추가하는 방법을 알아보자!

 

🎆 @EnableJpaAuditing은 엔티티를 필요로 한다

  • Error creating bean with name 'jpaAuditingHandler'
  • JPA metamodel must not be empty!
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
	...
Caused by: 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!

  @EnableJpaAuditing는 엔티티의 이벤트를 추적한다. 엔티티 데이터의 생성 일자와 수정 일자를 기록하기 위해 사용했다. 이 어노테이션은 최소 하나의 엔티티를 필요로 하는데, @WebMvcTestWebSecurityConfigurerAdapter, WebMvcConfigurer를 비롯한 @ControllerAdvice, @Controller를 읽을 뿐, @Repository@Service, @Component는 스캔하지 않으므로 엔티티가 존재하지 않는 상태가 된다.

@EnableJpaAuditing
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

현재 위의 코드와 같이 @EnableJpaAuditing@SpringBootApplication[각주:1]과 함께 있는 상태라 @WebMvcTest에서도 스캔하게 된 것이다. 따라서 두 어노테이션을 아래와 같이 분리해서 스캔하지 않도록 한다.

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args); // WAS 실행
    }
}
@Configuration
@EnableJpaAuditing
public class JpaConfig {
}

 

🎆 테스트에 임의로 인증된 사용자 추가하기

testImplementation("org.springframework.security:spring-security-test")
@Autowired
private WebApplicationContext context;

private MockMvc mvc;

@BeforeEach
public void setup() {
    mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())
            .build();
}
@Test
@WithMockUser(roles = "USER")
public void Posts_등록된다() throws Exception {
    ...

    // when
    mvc.perform(post(url)
            .contentType(MediaType.APPLICATION_JSON)
            .content(new ObjectMapper().writeValueAsString(requestDto))) // 본문 영역은 문자열로 표현
            .andExpect(status().isOk());

    ...
}

@WithMockUser

  • 인증된 모의(가짜) 사용자를 만든다.
  • MockMvc에서만 작동한다.

 

🎆 src/main과 src/test 환경 차이

  src/main과 src/test는 서로 다른 환경 구성을 가진다. test에 application.propeties가 없으면 main의 설정을 그대로 가져온다. 이때 자동으로 가져오는 범위는 application.properties까지이므로 application-oauth.properties와 같은 기타 다른 설정 파일들은 가져오지 않는다.

Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
	...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' available

따라서 test/resources에 별도의 설정 파일인 application.properties를 만들어 줬다.

resources를 resource로 생성하지 않도록 주의하자. 초반에 잘못 표기해서 이를 설정 파일로 인식하지 못했고, main의 설정 파일에 의존하게 되어 혼란스러웠다. 😧

spring.jpa.show_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.properties.hibernate.dialect.storage_engine=innodb
spring.datasource.hikari.jdbc-url=jdbc:h2:mem://localhost/~/testdb;MODE=MYSQL
spring.h2.console.enabled=true
spring.session.store-type=jdbc

# Test OAuth
spring.security.oauth2.client.registration.google.client-id=test # 임의의 설정
spring.security.oauth2.client.registration.google.client-secret=test
spring.security.oauth2.client.registration.google.scope=profile,email

 

 


  1. 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 모두 자동으로 설정한다. 이 어노테이션이 있는 위치(메인 클래스)부터 설정을 읽기 때문에 항상 프로젝트의 최상단에 위치해야 한다. [본문으로]

댓글