Spring, Junit 에서 session, request scope bean 을 사용하기 by 오리대마왕

Spring 3.0 을 사용하는데, Junit 을 사용한 unit test 을 실행할 때 scope이 session, request 인 bean이 주입된 경우,  해당 scope 이 등록이 되어있지 않다는 오류가 나온다. 강제로 request, session 이라는 scope을 applicationContext 에 넣어보려 해도 thread에 넣을 수 없다느니 하는 오류가 나온다.

이 문제는  applicationContext 자체가 session, request 를 제공하는 GenericWebApplicationContext 을 사용하면 해결될 것이다. 근데 어떻게 applicationContext class 를 변경할까? 아무리 api 를 뒤적여봐도 applicationContextLoader 를 변경하는 부분만 보인다. 그럼, applicationContextLoader 를 만들어버리면 된다.

  1. GenericWebApplicationContext 를 만드는 applicationContextLoader 를 만든다. 전체 소스는 다음 경로에 잘 나와있다. http://d.hatena.ne.jp/yehara/20090317/1237276275
  2. Junit4 를 사용한다면, @ContextConfiguration 애노테이션에 loader 속성을 사용하여 새로 만든 클래스를 지정한다.  
  3. @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 프로젝트를 첨부하였으니 궁금하면 직접 실행해 보자.

프로젝트 내용은 다음과 같다.
  1. /req_scope_test/test 에 접근하면 hello! 를 찍는다. (response header 가 엉터리라 잘 안보일 경우, 소스 보기를 해 보면 보인다.)  => ControllerA.java
  2. AOP 를 적용해서, @Controller 의 메서드가 실행할 경우 request URI 를 system out 에 찍는다. => ControllerAspect.java
  3. ControllerAspect 는 HttpRequstHolder 를 사용하여 http request 를 가져온다. HttpRequestHolder는 spring bean으로, 매 request 마다 새로 생성되어야 하므로 scope 가 request 이다. => HttplRequestHolder.java
  4. Controller 의 동작을 검증하기 위해 Unit Test 를 한다. 실행해 보면 AOP 적용도 잘 되는 걸 console 의 output으로 확인할 수 있다. => ControllerTest.java
  5. ControllerTest.java 가 실행되기 위해 위에 테스트의 applicationContext를 GenericWebApplicationContext으로 만들어준다. => XmlWebApplicationContextLoader.java
  6. 동작을 위한 Spring 설정을 정의한다. => WEB-INF/dispatcher-servlet.xml

혹시 controller test 에 애를 먹고 계셨던 분들에게 도움이 되었으면 좋겠다.

덧글

  • 세라딘 2010/08/16 12:08 # 삭제

    진정 개발자로 돌아온 것 같네 ㅋㅋㅋ 난 또 도망갈 생각 중 --;
  • 오리대마왕 2010/08/18 01:36 #

    히히 일단 돌아왔으니 본분에 충실해야죠. 전 오랫만에 왔으니 딴 생각 말고 3년은 붙박을 거에용. 히히힝
  • MR_KIM 2011/08/18 14:55 # 삭제

    Spring 3.1 에선
    req.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, Boolean.TRUE);
    setup 할 때 넣어줘야되네요.
    이상한 스프링.
  • 오리대마왕 2011/08/19 10:38 #

    결국 직접 해결하셨군요.
※ 로그인 사용자만 덧글을 남길 수 있습니다.