한참 사이드 프로젝트를 하던 도중 에러를 발견했다.
(ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context for [WebMergedContextConfiguratio) ...

분명히 몇 분 전까지만 해도 모두 통과하던 테스트 코드가 한꺼번에 모두 실패하기 시작했다.
다행(?)인건 부분부분 실패한게 아니라 전체 실패이기 때문에
하나의 문제에 의해 발생했을거라는것(추측)
우선 에러 로그를 확인해보았다.
실제 테스트 관련 로그는 아래 에러 메세지로 동일했다.

로그 메세지를 간략히 요약하면
테스트 컨텍스트 로딩에 실패했고
실패 반복을 피하기 위해 로딩 시도를 스킵했다는 것이다.
그리고 이전 테스트(성공)과 비교했을때 어떤 상태가 바뀌었는지
production코드에서 비교를 해보았고
카카오 로그인 관련 설정을 위해
SecurityConfig에
Oauth2 로그인 관련 설정을 추가한것이 있었다.
@Configuration
@EnableWebSecurity
class SecurityConfig(
private val customOAuth2UserService: CustomOAuth2UserService
) {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.csrf { it.disable() }
.headers { it.frameOptions { it.disable() } } // for h2-console
.authorizeHttpRequests {
it.requestMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**").permitAll()
it.requestMatchers("/api/v1/**").hasRole("USER")
it.requestMatchers("/api/admin/**").hasRole("ADMIN")
it.anyRequest().authenticated()
}
.logout { it.logoutSuccessUrl("/") }
.oauth2Login { // 여기 추가됨
it.userInfoEndpoint {
it.userService(customOAuth2UserService)
}
}
return http.build()
}
}
혹시나 이것때문인가 하여 주석 후 다시 테스트 코드를 실행해보았다.

정상 작동되는걸 확인했고, 문제 원인을 파악했다.
1. 원인파악
원인:
문제 원인은 production코드에 시큐리티 관련 설정 정보가 추가되면서,
테스트 실행 시 @SpringBootTest가 테스트 동작을 위해용 Application Context를 띄울 때.
SecurityConfig 빈이 모두 필요하지만
production과 달리 AutoConfiguration이 작동하지 않아서 필요한 빈이 등록되지 않음
production코드와 달리 테스트 컨텍스트에서는 필요한 빈이 없어서 생긴 문제였다.
(정확히는 ClientRegistrationRepository)
매번 시큐리티 컨피그에 Oauth2 정보를 주석하고 테스트를 돌릴 순 없다.
한번 해결해보자.
2. 문제 해결
1. ClientRegistrationRepsoitory @MockBean등록
시큐리티 컨피그에 ClientRegistrationRepsoitory가 빈 등록 안돼서 에러가 난거면
빈으로 등록해버리면 된다.

테스트는 성공시켰지만 단순히 성공시키는게 목적은 아니다.
'org. springframework. boot. test. mock. mockito. MockBean' is deprecated since version 3.4.0 and marked for removal
MockBean이SpringBoot 3.4.0부터 deprecated 예정이고 가급적이면 저 방식은 안쓰는게 좋겠다.
2. 컴포넌트 스캔 시 빈 등록
어쨌거든 @SpringBootTest 동작 시 빈을 등록시켜주면 된다.
이번에는 컴포넌트 스캔 대상으로 추가하여 빈을 등록해보자.
import org.mockito.Mockito
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
@Configuration
class TestSecurityConfig {
@Bean
@Primary
fun clientRegistrationRepository(): ClientRegistrationRepository =
Mockito.mock(ClientRegistrationRepository::class.java)
}

컴포넌트 스캔 시 ClientRegistrationRepsoitory를 빈 등록해주니 정상적으로 테스트가 동작했다.
그러나 TestSecurityConfig를 반드시 컴포넌트 스캔을 해야할까?
시큐리티가 필요없는 경우도 있지 않을까?
3. TestConfiguration + @Import
@Configuration이 붙은 이상 스프링이 반드시 스캔하여 빈으로 등록된다.
뭐 나쁘지 않지만 테스트가 지금보다 100배 많아졌다고 생각했을 때 불필요한 스캔이 아닐까 생각했다.
왜냐하면 시큐리티가 필요한 부분에만 스캔하도록 처리할 수는 없을까?
@Configuration -> @TestConfiguration
import org.mockito.Mockito
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Primary
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
@TestConfiguration
class TestSecurityConfig {
@Bean
@Primary
fun clientRegistrationRepository(): ClientRegistrationRepository =
Mockito.mock(ClientRegistrationRepository::class.java)
}

이런 방식으로 @TestConfiguration + 시큐리티 빈 등록이 필요한 테스트에 컨피그를 임포트 하는 방식
으로 전환했다.
이렇게 바꿨을때의 장점은 다음과 같다.
1. production과 테스트 환경 분리
(@Configuration 시 production 환경에서도 적용될 수 있음)
2. 테스트 시 불필요한 빈 등록 방지
(테스트마다 필요한 Mock 빈이 다름)
3. 의도 전달
(@TestConfiguration은 누가봐도 테스트 환경임)
같은 이유로 바꿨고 수정 이후에도 정상적으로 테스트가 동작했다.

결론:
테스트 환경 격리는 생각보다 신경쓸게 많고
유지보수도 잘 생각해서 작성해야한다.
'공부 > 테스트 코드' 카테고리의 다른 글
| 테스트 더블 (0) | 2025.08.20 |
|---|---|
| TDD 초 기초편 (0) | 2025.08.09 |
| ArchUnit 테스트? 그것이 무엇인가? (4) | 2025.08.08 |