사용법
ApplicationContext 전체를 초기화하지 않고 일부 Bean 만을 테스트할 때
@ExtendWith(SpringExtension::class) + @ContextConfiguration + @TestConfiguration
=> @TestConfiguration 에서 원하는 빈을 초기화한다. (@Bean 을 쓰거나 @ComponentScan을 쓰거나..)
=> 프로파일 등 수동 프로퍼티 지정이 필요할 때는 @ContextConfiguration 의 initializers 에 ApplicationContextInitializer 구현체를 지정한다.
예(코틀린):
class PropertyOverrideContextInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(applicationContext: ConfigurableApplicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"spring.config.activate.on-profile=production",
)
}
}
=> application.properties(or yml) 자동 로딩을 사용하고 싶으면 initializers 에 ConfigDataApplicationContextInitializer 클래스를 추가한다. 왜냐하면, 설정파일 자동 로딩을 통한 어플리케이션 자동 설정은 스프링 부트의 기능이다. SpringExtension::class 은 스프링 부트가 아닌 전통 방식으로 스프링을 가동하기 때문에, 별도 설정이 없다면 비어있는 프로퍼티 값을 보게 될 것이다.
ApplicationContext 전체를 초기화하지 않고 프로퍼티 설정을 테스트할 때
@ExtendWith(SpringExtension::class) + @ContextConfiguration with ConfigDataApplicationContextInitializer + @TestConfiguration + @EnableConfigurationProperties(optional)
=> 여기서 @TestConfiguration 은 초기화 대상 빈을 지정하는 역할을 한다. 만약 정말 프로퍼티 미러링 빈만을 필요로 한다면 아래와 같이 사용할 수 있다. (다른 빈들도 필요하다면 그냥 @ComponentScan 으로 프로퍼티 미러링 빈과 다른 빈들을 모두 함께 초기화하는 편이 낫다)
@ExtendWith(SpringExtension.class)
@ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class)
public class MongoPropertiesTest {
@Autowired
private MongoProperties properties;
@Configuration
@EnableConfigurationProperties({MongoProperties.class})
public static class Config {
}
}
Jpa 테스트 수행 시
@DataJpaTest 를 사용한다. 추가로 다른 빈이 필요한 경우 @TestConfiguration + @Import 를 사용한다.
웹 MVC 테스트 수행 시
@WebMvcTest 를 사용한다. 추가로 다른 빈이 필요한 경우 @TestConfiguration + @Import 를 사용한다.
@Service 등 비즈니스 로직 테스트 수행 시
1. 데이터 저장 애뮬레이션이 필요하다면 아래와 같이 사용한다.
@ExtendWith(SpringExtension.class) @SpringBootTest + @TestConfiguration(선택) + @Import
2. 데이터 저장 애뮬레이션이 필요하지 않다면 아래와 같이 사용한다.
@ExtendWith(SpringExtension.class) + @SpringBootTest + @TestConfiguration(선택) + @Import + @MockBean(to repositories)
통합 테스트 수행 시
@SpringBootTest 를 사용한다.
어노테이션 설명
@RunWith
JUnit 4 의 어노테이션으로, JUnit 의 Test Runner 구현체를 인자로 받아 테스트 메서드 실행에 관한 동작을 확장할 수 있다. 주로 아래와 같이 사용된다.
@RunWith(SpringJUnit4ClassRunner.class)
Runner 에 대한 자세한 설명은 아래를 참조
https://eminentstar.github.io/2017/07/23/about-junit-and-test.html
@RunWith(SpringJUnit4ClassRunner.class) vs @RunWith(SpringRunner.class)
같은 녀석들이다. SpringJUnit4ClassRunner 의 이름을 줄인 버전이 SpringRunner 라고 생각하면 된다.
@ExtendWith
JUnit 5 의 어노테이션으로, @RunWith 을 대체한다. @RunWith 에서 Runner 의 구현체를 인자로 받았던 것처럼, 여기서는 Extension 인터페이스의 구현체를 인자로 받는다. Extension 은 주로 test 메서드의 전/후 동작, 조건부 테스트 실행, 테스트 메서드 라이프싸이클 콜백, 파라미터 리솔루션, 예외 처리 등 테스트에 영향을 미치는 동작에 대한 확장을 제공한다. 대표적인 구현체로 SpringExtension 이 있다.
SpringExtension 은 어플리케이션 컨텍스트를 로드한다. 아래 @SpringBootTest 또한 어플리케이션 컨텍스트를 로드하는데, 둘의 차이는 SpringExtension 은 어플리케이션 컨텍스트를 '전통적인(tranditional) 스프링 방식' 으로 로드하고, @SpringBootTest 는 '스프링 부트 방식' 으로 로드한다는 점이다. 때문에 SpringExtension 는 스프링 부트에서 제공하는 '외부 설정 로딩' 을 사용하지 못하여 application.properties, 환경변수, cli 아규먼트 등으로 어플리케이션 옵션을 주입할 수 없다. 프로퍼티를 사용하려면 ConfigDataApplicationContextInitializer, @EnableConfigurationProperties 등을 사용해서 직접 프로퍼티 설정 로딩 코드를 작성해야 한다.
@SpringBootTest
스프링 어플리케이션 컨텍스트를 로드하는 어노테이션으로, 운영 환경에 가장 가까운 환경을 애뮬레이션한다. 필요한 모든 빈을 초기화하기 때문에 무겁게 작동한다. 내장 톰캣을 사용한 웹 환경 애뮬레이션을 지원하며, 통합 테스트에 적절하다. 이 어노테이션의 실체는 @BootStrapWith(SpringBootTestContextBootstrapper.class) 이다. SpringBootTestContextBootstrapper 에서 어플리케이션 컨텍스트를 로드하고 클래스패스에서 @SpringBootConfiguration(@SpringBootApplication) 을 찾아 초기화한다.
스프링 부트 버전 2.1 부터 이 어노테이션에 @ExtendWith(SpringExtension.class) 가 붙었다. 즉 버전 2.1 부터는 @ExtendWith 을 함께 사용할 필요가 없다.
위 SpringExtension 에서 설명하였듯, 이 어노테이션은 어플리케이션 컨텍스트를 '스프링 부트 방식' 으로 로드한다. 때문에 프로퍼티, 환경변수, cli 아규먼트를 사용하는 일이 가능하다.
@Configuration 또는 classes 가 함께 지정되지 않으면, 이 어노테이션이 지정된 클래스의 상위, 하위에 존재하는 @SpringBootConfiguration 을 모두 찾는다.
@ContextConfiguration(loader=...) 를 지정하지 않으면 기본적으로 SpringBootContextLoader 를 사용한다.
@MockBean
목 인스턴스를 주입한다. 테스트 클래스가 다른 빈에 의존할 때, 논리적으로는 필요하지만 실제 동작 테스트까지는 필요하지 않은 빈이 있을 수 있다. 그럴 때 @Autowired 를 대신해서 이 어노테이션을 사용한다. 목 인스턴스는 실제로 작동하지는 않고 '작동하는 척' 한다. 아래와 같이 given 등 메서드를 사용해서 마치 실제로 호출해서 어떤 결과를 반환받은 것처럼 사용한다.
given(mockRepository.save(any(MyEntity.class))).willReturn(mySavedEntity())
@DataJpaTest
data jpa 에 관련한 테스트를 지원하기 위한 어노테이션으로, jpa 리포지토리 테스트에 사용하기 적절하다. data jpa 작동에 필요한 빈만을 초기화하기 때문에 @SpringBootTest 보다 훨씬 가볍게 작동한다. 하지만 @Service 등 다른 빈을 초기화하지 않기 때문에, 테스트가 퍼시스턴스 레이어 위쪽에 의존성을 가진 경우 이 어노테이션만으로는 테스트 불가능하다.
이 어노테이션은 기본 설정으로 테스트 시 인메모리 데이터베이스를 사용하도록 하며, 테스트 케이스마다 @Transactional 을 적용하며 이 트랜잭션은 한 테스트 케이스가 종료되면 자동으로 롤백을 수행한다.
@AutoConfigureTestDatabase
어플리케이션에 설정된 데이터베이스를 테스트용 데이터베이스로 대체하는 어노테이션이다. replace 라는 인자값이 중요한데, 아래와 같은 값들이 있다.
Replace.ANY: 기본값으로, 명시적 or auto-configured 된 테스트용 데이터베이스로 대체한다.
Replace.AUTO_CONFIGURED: 오직 auto-configured 된 테스트용 데이터베이스로만 대체한다.
Replace.NONE: 데이터베이스를 대체하지 않는다.
@DataJpaTest 는 이 어노테이션을 ANY 로 사용하며 인메모리 데이터베이스로 대체한다. 직접 데이터베이스를 구동하여 테스트 결과를 조회하고 싶거나 하는, 이런 동작을 원치 않는 경우에는 Replace.NONE 을 사용하면 된다.
@WebMvcTest
data jpa 전용 테스트를 지원하기 위해 @DataJpaTest 가 있듯, 웹 MVC를 테스트하기 위해 이 어노테이션이 있다. 아래와 같은 대상만 스캔하여 빈으로 초기화한다.
@Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, HandlerInterceptor
주로 컨트롤러-인터셉터-필터를 테스트하기 위해 사용한다.
@TestConfiguration
테스트 설정을 위한 어노테이션으로, @Configuration 을 테스트 환경에 맞게 확장한 것이라 생각할 수 있다. 사실 그 동작은 @Configuration 과 다르지 않다. (실제로 @TestComponent 가 붙어있을 뿐이다.) 차이점이 있다면 이 어노테이션은 기본적으로 @SpringBootConfiguration 에서 스캔하지 않는다. 때문에 직접 @Import or @ContextConfiguration 을 명시해야 한다. (대신, @SpringBootTest(classes=... ) 를 사용하거나, @ComponentScan을 직접 사용한 경우 스캔 대상이 되므로 이 때 이 어노테이션이 지정된 클래스를 제외하기를 원한다면 TypeExcludeFilter 를 사용해야 한다.)
이 어노테이션의 사용법은 대표적으로 두 가지가 있다.
1. @SpringBootTest 지정 클래스의 내부클래스에 지정하기
이 때는 @Import 를 사용하지 않아도 자동으로 설정이 로드된다. 특정 @SpringBootTest 테스트 클래스 안에서만 사용할 설정을 정의한다.
2. 독립된 외부 클래스에 지정하고 @Import 하기
여러 테스트에서 공유해서 사용할 설정을 정의한다.
이 어노테이션의 주 용도는:
1. 테스트 환경에서 필요한 빈들만을 초기화하는 데에 있다. @DataJpaTest, @WebMvcTest 와 같이 특정 레이어에 대한, 통합테스트보다 가벼운 환경으로 테스트하는 경우 초기화 대상에서 벗어난 빈이 필요할 수 있다. 이를 위해 @SpringBootTest 를 사용한다면 불필요하게 무거운 동작이 추가될 것이다. 이 때 @TestConfiguration 을 사용해서 원하는 빈들을 초기화하는 클래스를 작성하고, 테스트 클래스에서 이 클래스를 @Import 하면 여기의 빈들만을 초기화한다.
https://csy7792.tistory.com/335
2. 테스트 환경에서 특정 빈을 오버라이딩할 수 있다. 어떤 빈들은 테스트 시 다른 구현체를 사용하고 싶다면, 이 어노테이션을 사용하여 같은 타입, 이름의 빈 초기화 코드를 넣고 @Import 하면 테스트 시 이 빈을 사용한다.
빈 오버라이딩 시에는 spring.main.allow-bean-definition-overriding=true 를 설정하거나, 빈 이름을 따로 지정해서 기존 빈 이름과 겹치지 않게 하여야 한다. 그렇지 않으면 빈 중복으로 에러가 발생한다.
https://www.logicbig.com/tutorials/spring-framework/spring-boot/test-configuration.html