스프링 MVC 프레임워크로 개발한 웹 컨트롤러를 통합 테스트하기
DispatcherServlet은 스프링 MVC 컨트롤러에 HTTP 요청/응답 객체를 건네줌
MVC 컨트롤러는 비즈니스 처리를 마친 다음 뷰를 렌더링하기 위해 HTTP 요청/응답 객체를 다시 DispatcherServlet에 돌려줌
다른 웹 애플리케이션 프레임워크도 비슷하지만 HTTP 요청/응답 객체를 시뮬레이션하고 목 환경을 설정하는 일이 스프링 MVC 컨트롤러의 통합 테스트에서 가장 까다로운 부분
스프링은 목 MVC 테스트를 지원하므로 목 서블릿 환경을 쉽게 구성 가능
스프링 테스트 목 MVC를 이용해 WebApplicationContext를 구성하면 목 MVC API를 이용해 HTTP 요청을 시뮬레이션하고 그 결과를 확인 가능
웹 관련 빈들을 구성클래스에 설정
@Configuration
@EnableWebMvc
@ComponentScan(value = "com.apress.springrecipes.bank.web")
public class BankConfiguration {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
@EnableWebMvc를 적용해 애너테이션 기반의 컨트롤러를 활성화
@ComponentScan으로 @Controller 빈을 자동 등록
InternalResourceViewResolver가 뷰 이름을 URL로 바꿔주면 브라우저는 해당 페이지를 렌더링
통합 테스트, BankConfiguration 클래스에 설정한 내용을 로드하고 클래스 레벨에 @WebAppConfiguration을 붙여 일반 ApplicationContext가 아닌 WebApplicationContext를 가져오도록 테스트 컨텍스트 프레임워크에 알림
테스트가 끝나면 등록했던 테스트 데이터를 롤백해야 하므로 AbstractTransactionalJUnit4SpringContextTests를 상속하는 게 가장 간편
@ContextConfiguration(classes = BankConfiguration.class)
public class AccountServiceJUnit4ContextTests extends AbstractTransactionalJUnit4SpringContextTests {
private static final String ACCOUNT_PARAM = "accountNo";
private static final String AMOUNT_PARAM = "amount";
private static final String TEST_ACCOUNT_NO = "1234"
private static final String TEST_AMOUNT = "50.0";
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc = mockMvc;
@Before
public void init() {
executeSqlScript("classpath:/bank.sql", true);
jdbcTemplate.update(
"INSERT INTO ACCOUNT (ACCOUNT_NO, BALANCE) VALUES (?, ?)",
TEST_ACCOUNT_NO, 100);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).bulid();
}
@Test
public void deposit() throws Exception {
mockMvc.perform(
get("/deposit.do")
.param(ACCOUNT_PARAM, TEST_ACCOUNT_NO)
.param(AMOUNT_PARAM, TEST_AMOUNT)
.andDo(print())
.andExpect(forwardedUrl("/WEB-INF/views/success.jsp"))
.andExpect(status().isOK());
)
}
}
MockMvc 객체는 init() 메서드에서 간편히 MockMvcBuilders로 생성
팩토리 메서드 webAppContextSetup()를 호출해 이미 불러들인 WebApplicationContext로 MockMvc 객체를 초기화
MockMvc 객체는 스프링 MVC 애플리케이션의 DispatcherServlet을 흉내내서 WebApplicationContext로 핸들러 매핑 및 뷰 해석 전략을 설정하고 인터셉터가 구성되어 있으면 모두 찾아내 적용
테스트할 계정은 init() 메서드에서 SQL문으로 미리 설정
deposit() 테스트 메서드는 앞서 초기화한 MockMvc 객체를 이용해 매개변수가 2개(accountNo, ammount)인 GET/deposit.do 요청이 들어오는 장면을 시뮬레이션
MockMvcRequestsBuilders.get() 팩토리 메서드가 반환한 RequestsBuilder 인스턴스는 mockMvc.perform() 메서드의 인수로 전달
perform() 메서드가 ResultActions 객체를 반환하면 그 결과에 대해 어떤 액션을 취하거나 어설션을 수행 가능
andDo(print()) 메서드는 테스트 코드를 디버깅할 때 요긴한 요청과 응답 정보를 출력
어설션 2개로 작동 여부를 확인
DepositController가 success를 viewname으로 반환하면 ViewResolver 설정에 따라 /WEB-INF/views/success.jsp로 포워딩
status().isOK()는 반환코드가 200(OK)인지 확인
TestNG에서는 AbstractTransactionalTestNGSpringContextTests를 상속 후 @WebAppConfiguration을 붙여 스프링 목 MVC를 사용
@ContextConfiguration(classes = BankConfiguration.class)
@WebAppConfiguration
public class AccountServiceJUnit4ContextTests extends AbstractTransactionalTestNGSpringContextTests {
@BeforeMethod
public void init() {
executeSqlScript("classpath:/bank.sql", true);
jdbcTemplate.update(
"INSERT INTO ACCOUNT (ACCOUNT_NO, BALANCE) VALUES (?, ?)",
TEST_ACCOUNT_NO, 100);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).bulid();
}