우아한테크코스 레벨2 웹 자동차 경주 미션을 진행하면서 @TestConfiguration
을 사용하게 되어 내용을 정리해보려고 한다.
해당 미션에서는 랜덤한 수를 반환하는 RandomStrategy
를 사용하고 있다. 하지만 테스트 하기 위해서는 고정적인 값이 필요하기 때문에, 테스트 시 테스트용으로 작성한 FixedMovingStrategy
를 사용해야한다.
어떻게 이미 컴포넌트로 등록된 MovingStrategy
를 테스트에만 변경하여 적용할 수 있을까?
RacingCarController
에 테스트를 진행하고자 하며, 테스트를 위해 Controller 테스트 내부에 Config 클래스를 만들고 @TestConfiguration
애너테이션을 사용해 기존에 RandomStrategy
를 사용하는 곳에 FixedMovingStrategy
를 사용할 수 있도록 했다.
public interface MovingStrategy {
int getRandomNumber();
}
@Component
public class RandomMovingStrategy implements MovingStrategy {
private final int MAX_MOVING_NUM = 9;
private final Random random = new Random();
@Override
public int getRandomNumber() {
return random.nextInt(MAX_MOVING_NUM);
}
}
public class FixedMovingStrategy implements MovingStrategy {
@Override
public int getRandomNumber() {
return 5;
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RacingCarControllerTest {
@LocalServerPort
int port;
@TestConfiguration
static class RacingCarControllerTestConfig {
@Bean
MovingStrategy movingStrategy() {
return new FixedMovingStrategy();
}
}
}
위와 같이 구현했더니, 리뷰어님이 이후에 다른 테스트들이 추가되면 모두 필요한 공통 설정이 될 수 있으니 클래스 분리를 하는 것이 어떻겠냐고 제안주셨다.
해당 의견에 동의해서 기존에 RacingCarControllerTest
내부에 위치한 RacingCarControllerTestConfig
를 외부로 분리하였다(TestConfig
).
@TestConfiguration
public class TestConfig {
@Bean
MovingStrategy movingStrategy() {
return new FixedMovingStrategy();
}
}
그런데 클래스 분리를 하고 난 후에 테스트가 깨지는 문제가 발생했다. 설정한 FixedMovingStragtegy
가 적용되지 않는 것 같았고,@Import
를 사용하면 문제를 해결할 수 있음을 알아내었다.
@Import
를 아래와 같이 붙여주었더니 잘 동작하였다.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import({TestConfig.class})
class RacingCarControllerTest {
// ... 테스트 코드
}
그럼 이제 @TestConfiguration
과 @Import
에 대해 알아보자.
테스트를 할 때, 실제 애플리케이션 컨텍스트가 아닌 테스트용 configuration을 사용하고 싶을 수 있다. 이 때
@TestConfiguration
을 사용할 수 있다.
공식 문서에서는 @TestConfiguration
을 아래와 같이 설명하고 있다.
테스트에 대한 추가적인 빈이나 커스텀을 정의하는 데 사용할 수 있는 @Configuration
이다.
@Configuration
클래스와 달리 @TestConfiguration
을 사용해도 @SpringBootConfiguration
의 자동 감지를 방지하지 않는다.
@TestConfiguration
을 사용하는 방법은 2가지이다.
static inner class
를 만들어 빈을 @Autowire 하는 방법@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {
@TestConfiguration
static class EmployeeServiceImplTestContextConfiguration {
@Bean
public EmployeeService employeeService() {
return new EmployeeService() {
// implement methods
};
}
}
@Autowired
private EmployeeService employeeService;
}
@Import
를 사용해 명시해주는 방법@TestConfiguration
public class EmployeeServiceImplTestContextConfiguration {
@Bean
public EmployeeService employeeService() {
return new EmployeeService() {
// implement methods
};
}
}
@RunWith(SpringRunner.class)
@Import(EmployeeServiceImplTestContextConfiguration.class)
public class EmployeeServiceImplIntegrationTest {
@Autowired
private EmployeeService employeeService;
// remaining class code
}
@Import
애너테이션을 사용하면 특정 컴포넌트를 다른 configuration으로 import할 수 있다.
공식 문서에서는 @Import
를 아래와 같이 설명하고 있다.
import할 하나 이상의 컴포넌트 클래스(일반적으로 @Configuration
클래스)를 명시한다.
Spring XML
의 Import
와 같은 기능을 제공한다.
가져온 @Configuration
클래스에서 선언된 @Bean
정의는 @Autowired
주입을 사용하여 액세스해야 한다. 빈 자체를 자동 와이어링하거나 빈을 선언하는 구성 클래스 인스턴스를 자동 와이어링할 수 있다.
XML
또는 @Configuration
이외의 리소스를 가져와야 하는 경우 @ImportResource
어노테이션을 대신 사용 한다.
해당 애너테이션을 사용할 때, 아래와 같이 여러 클래스를 import할수도 있다.
@Configuration
@Import({ DogConfig.class, CatConfig.class })
class MammalConfiguration {
}
Testing in Spring Boot
Annotation Interface TestConfiguration
Spring @Import Annotation
@Import in Spring
Annotation Type Import
오
@TestConfiguration
으로 처리하셨군요! 전략패턴을 사용하는 상황에서 빈 이름이 중복될 때에, 수동 빈 등록 말고 자동 빈 등록 상황에서도 해결할 수 있는 방법이 몇 가지 있더라구요.@Primary
나@Qualifier
, 빈의 필드명 매칭 방법이 간단하게 있을 것 같네요 ㅎㅎㅎ한편으로,
@Import
를 내가 원하는 빈들만 등록하고 싶을 때 사용해도 되나 궁금하던 참이었는데, 조이는 어떻게 생각하시는지 궁금합니다! 저는 보통@MvcTest
나@JdbcTest
에서 필요한 것들만 빈으로 올리고 싶을 때 사용하곤 했었는데 Configuration 클래스를 Import할 때에만 사용하는 것이 좋은지 궁금해요 ㅎㅎㅎ