Spring 3.0 을 사용하는데, Junit 을 사용한 unit test 을 실행할 때 scope이 session, request 인 bean이 주입된 경우, 해당 scope 이 등록이 되어있지 않다는 오류가 나온다. 강제로 request, session 이라는 scope을 applicationContext 에 넣어보려 해도 thread에 넣을 수 없다느니 하는 오류가 나온다.
전체 eclipse 프로젝트를 첨부하였으니 궁금하면 직접 실행해 보자.
이 문제는 applicationContext 자체가 session, request 를 제공하는 GenericWebApplicationContext 을 사용하면 해결될 것이다. 근데 어떻게 applicationContext class 를 변경할까? 아무리 api 를 뒤적여봐도 applicationContextLoader 를 변경하는 부분만 보인다. 그럼, applicationContextLoader 를 만들어버리면 된다.
- GenericWebApplicationContext 를 만드는 applicationContextLoader 를 만든다. 전체 소스는 다음 경로에 잘 나와있다. http://d.hatena.ne.jp/yehara/20090317/1237276275
- Junit4 를 사용한다면, @ContextConfiguration 애노테이션에 loader 속성을 사용하여 새로 만든 클래스를 지정한다.
- @Before 부분에서 mock request 를 RequestContextHolder 에 전달한다.
몇가지 설정을 바꾸어 테스트해 보았는데, 위 3가지를 모두 만족해야만 제대로 테스트가 동작하였다. 심심하면 하나씩 바꾸어가면서 다양한 오류 메세지를 경험해 보자. 내가 만든 전체 Junit Test case 소스코드는 다음과 같다.
@RunWith( SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"file:WebContent/WEB-INF/dispatcher-servlet.xml"}, loader=XmlWebApplicationContextLoader.class )
public class ControllerTest extends AbstractJUnit4SpringContextTests {
private MockHttpServletRequest req;
private MockHttpServletResponse res;
@Resource(name = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#0")
private HandlerAdapter handlerAdapter;
ControllerA controller;
@Before
public void setUp() {
req = new MockHttpServletRequest();
req.setSession(new MockHttpSession());
res = new MockHttpServletResponse();
controller= super.applicationContext.getBean(ControllerA.class);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(req));
}
@Test
public void getResult() throws Exception {
req.setRequestURI("/test");
handlerAdapter.handle(req, res, controller);
assertEquals("hello!", res.getContentAsString());
}
}
전체 eclipse 프로젝트를 첨부하였으니 궁금하면 직접 실행해 보자.
프로젝트 내용은 다음과 같다.
- /req_scope_test/test 에 접근하면 hello! 를 찍는다. (response header 가 엉터리라 잘 안보일 경우, 소스 보기를 해 보면 보인다.) => ControllerA.java
- AOP 를 적용해서, @Controller 의 메서드가 실행할 경우 request URI 를 system out 에 찍는다. => ControllerAspect.java
- ControllerAspect 는 HttpRequstHolder 를 사용하여 http request 를 가져온다. HttpRequestHolder는 spring bean으로, 매 request 마다 새로 생성되어야 하므로 scope 가 request 이다. => HttplRequestHolder.java
- Controller 의 동작을 검증하기 위해 Unit Test 를 한다. 실행해 보면 AOP 적용도 잘 되는 걸 console 의 output으로 확인할 수 있다. => ControllerTest.java
- ControllerTest.java 가 실행되기 위해 위에 테스트의 applicationContext를 GenericWebApplicationContext으로 만들어준다. => XmlWebApplicationContextLoader.java
- 동작을 위한 Spring 설정을 정의한다. => WEB-INF/dispatcher-servlet.xml
혹시 controller test 에 애를 먹고 계셨던 분들에게 도움이 되었으면 좋겠다.
덧글
req.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, Boolean.TRUE);
setup 할 때 넣어줘야되네요.
이상한 스프링.