#freeze [[FrontPage]] #contents 2008/03/13からのアクセス回数 &counter; * Spring-MVCでspringのsession scopeオブジェクトをテストしたい [#caea628a] Spring 2.0から導入されたsession scopeオブジェクトは、DispatcherServletで処理されるため MockHttpServletRequestを使った単体テストでは、bean定義ファイルでscope="session"で 定義されたsession scopeオブジェクトのsession単位での持ち回りのテストができません。 ** session scopeオブジェクトに必要なもの [#z1d0df69] session scopeオブジェクトのテストには、 -- GenericWebApplicationContextをcreateApplicationContextメソッドで返す -- WebApplicationContextのbeanFactoryにSCOPE_REQUEST, SCOPE_SESSION, SCOPE_GBLOBAL_SESSIONスコープを登録する -- HTTPリクエストを処理する前に、WebApplicationContextにサーブレットコンテキストをセットし、サーブレットのROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEにWebApplicationContextをセットする -- HTTPリクエストの処理の後に、RequestContextFilterと同じ処理を追加する *** AbstractTransactionalSpringWebContextTestsを追加 [#h0cadda5] GenericWebApplicationContextをcreateApplicationContextメソッドで返すTestCaseとして AbstractTransactionalSpringWebContextTestsを追加しました。 AbstractTransactionalSpringWebContextTestsのcreateApplicationContextは以下の通りです。 #pre{{ protected ConfigurableApplicationContext createApplicationContext(final String[] locations) { GenericWebApplicationContext context = new GenericWebApplicationContext( new GenericApplicationContext().getDefaultListableBeanFactory()); context.setServletContext(new MockServletContext()); customizeBeanFactory(context.getDefaultListableBeanFactory()); createBeanDefinitionReader(context).loadBeanDefinitions(locations); context.refresh(); // StaticWebApplicationContext のpostProcessBeanFactoryと同じ処理を追加 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false)); beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true)); return context; } }} *** beginRequest, endRequestメソッドの追加 [#l7d22f78] HTTPリクエストの前後にRequestContextFilterと同様の処理行うメソッド、beginRequest, endRequestメソッドをAbstractTransactionalSpringWebContextTestsに定義しました。 beginRequestメソッドは、 #pre{{ public void beginRequest(MockHttpServletRequest req) { ServletRequestAttributes attributes = new ServletRequestAttributes(req); req.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes); RequestContextHolder.setRequestAttributes(attributes); ServletContext sc = req.getSession().getServletContext(); GenericWebApplicationContext wac = (GenericWebApplicationContext)getApplicationContext(); wac.setServletContext(sc); sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (GenericWebApplicationContext)getApplicationContext()); } }} endRequestメソッドは、 #pre{{ public void endRequest(MockHttpServletRequest req) { ServletRequestAttributes attributes = (ServletRequestAttributes) req .getAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE); ServletRequestAttributes threadAttributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); if (threadAttributes != null) { // We're assumably within the original request thread... if (attributes == null) { attributes = threadAttributes; } RequestContextHolder.setRequestAttributes(null); LocaleContextHolder.setLocale(null); } if (attributes != null) { attributes.requestCompleted(); if (logger.isDebugEnabled()) { logger.debug("Cleared thread-bound request context: " + req); } } } }} と定義しました。 ** テストメソッドの記述方法 [#r94c1f00] session scopeオブジェクトのテストメソッドを定義するには、 -- AbstractTransactionalSpringWebContextTestsのサブクラスとしてTestCaseを定義する -- HTTP要求を処理するhadleRequestの前後をbeginRequest, endRequestで挟む -- 同一セッション内の連続する処理をテストする場合には、直前のsessionをMockHttpServletRequestにセットする *** HTTP要求の前後処理例 [#a392de6c] 以下にMemberOpsControllerを例にHTTP要求の前後処理の仕方を示します。 #pre{{ MemberOpsController memberOpsController = (MemberOpsController)getApplicationContext().getBean("memberOpsController"); ModelAndView mv = null; beginRequest(req); mv = memberOpsController.handleRequest(req,new MockHttpServletResponse()); endRequest(req); }} *** セッション情報の持ち回り [#h311f391] 同一セッションは、つぎのようにして持ち回ることができます。 #pre{{ HttpSession session = req.getSession(); req = new MockHttpServletRequest("POST","memberops/list.htm"); req.setSession(session); }} これで、session scopeオブジェクトの単体テストができるようになります。 ** サンプルの紹介 [#d85f1193] このページが分かりづらいとのご指摘が多いので、TestCase例で説明します。 - maven-GenMVC-pluginに入っている&ref(AbstractTransactionalSpringWebContextTests.java); - maven-GenMVC-pluginで生成されるテストケースひな形&ref(TestCase.java); を参考にしながら、以下の説明を見てください。 *** テストケースの作成 [#bcc68de6] AbstractTransactionalSpringWebContextTestsのサブクラスとして定義します。 #pre{{ public class TestCase extends AbstractTransactionalSpringWebContextTests { }} *** Sessionを後のリクエストに継承する例 [#m84614cd] 次にSessionを後のリクエストに継承するメソッドを示します。 - HTTP要求の前後にbeginRequest, endRequestで囲む のは、SpringではフィルタRequestContextFilterの処理と同じこと処理を実現するためです。 例では、handleRequestの前後に挿入してあります。 #pre{{ try { beginRequest(req); mv = memberOpsController.handleRequest(req,new MockHttpServletResponse()); endRequest(req); } catch (Exception err) { err.printStackTrace(); } }} - セッションの引き継ぎ は、新しく生成したMockHttpServletRequestに前回の要求のsessionをセットすることで再現します。 #pre{{ HttpSession session = req.getSession(); req = new MockHttpServletRequest("POST","memberops/list.htm"); // sessionを次の要求に引き継ぐ req.setSession(session); }} 念のために、testmemberOpsController_Listのメソッドを示します。 #pre{{ // memberOpsController list method test public void testmemberOpsController_List() { dbUnitHelperDao.restore("src/test/resources/dump.xml"); MockHttpServletRequest req = new MockHttpServletRequest("POST","memberops/list.htm"); MemberOpsController memberOpsController = (MemberOpsController)getApplicationContext().getBean("memberOpsController"); ModelAndView mv = null; try { beginRequest(req); mv = memberOpsController.handleRequest(req,new MockHttpServletResponse()); endRequest(req); } catch (Exception err) { err.printStackTrace(); } Member expectedMember = (Member)getApplicationContext().getBean("expectedMember"); assertNull(mv.getViewName()); Member[] members = (Member[])mv.getModel().get("memberList"); assertEquals(memberManager.print(expectedMember), memberManager.print(members[0])); HttpSession session = req.getSession(); req = new MockHttpServletRequest("POST","memberops/list.htm"); // sessionを次の要求に引き継ぐ req.setSession(session); memberOpsController = (MemberOpsController)getApplicationContext().getBean("memberOpsController"); mv = null; try { beginRequest(req); mv = memberOpsController.handleRequest(req,new MockHttpServletResponse()); endRequest(req); } catch (Exception err) { err.printStackTrace(); } expectedMember = (Member)getApplicationContext().getBean("expectedMember"); assertNull(mv.getViewName()); members = (Member[])mv.getModel().get("memberList"); assertEquals(memberManager.print(expectedMember), memberManager.print(members[0])); } }} ** コメント [#y379a8a1] この記事は、 #vote(おもしろかった[3],そうでもない[1],わかりずらい[17]) #vote(おもしろかった[3],そうでもない[1],わかりずらい[18]) 皆様のご意見、ご希望をお待ちしております。 - 分かりづらいとのコメントに対し、「サンプルの紹介」とサンプルソースを追加しました。 -- [[竹本 浩]] &new{2009-05-20 (水) 14:56:26}; #comment_kcaptcha