StrutsTestCase测试Maven构建的Spring+Hibernate+Struts
用maven构建了一个simple java web application。目录结构都按照maven默认定义的设置。
由于ApplicationContext.xml 存放着resources目录下,引起了很多spring配置文件找不到的问题。这些今天就不说了。先说今天的事。
我们在用StrutsTestCase对Struts进行单元测试的时候出现过ApplicationContext.xml 找不到的问题,控制台打印如下:
servletunit.struts.ExceptionDuringTestError: An uncaught exception was thrown during actionExecute() at servletunit.struts.MockStrutsTestCase.actionPerfor m(MockStrutsTestCase.java:409) at test.struts.action.StrutsLogonTest.testExecuteActi onMappingActionFormHttpServletRequestHttpServletRe sponse(StrutsLogonTest.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknow n Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Un known Source) at java.lang.reflect.Method.invoke(Unknown Source) at junit.framework.TestCase.runTest(TestCase.java:154 ) at junit.framework.TestCase.runBare(TestCase.java:127 ) at junit.framework.TestResult$1.protect(TestResult.ja va:106) at junit.framework.TestResult.runProtected(TestResult .java:124) at junit.framework.TestResult.run(TestResult.java:109 ) at junit.framework.TestCase.run(TestCase.java:118) at junit.framework.TestSuite.runTest(TestSuite.java:2 08) at junit.framework.TestSuite.run(TestSuite.java:203) at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.runTests(RemoteTestRunner.java:421) at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.run(RemoteTestRunner.java:305) at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.main(RemoteTestRunner.java:186) ------------ Root Cause: ------------ java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered? at org.springframework.web.context.support.WebApplica tionContextUtils.getRequiredWebApplicationContext( WebApplicationContextUtils.java:83) at org.springframework.web.struts.DispatchActionSuppo rt.initWebApplicationContext(DispatchActionSupport .java:102) at org.springframework.web.struts.DispatchActionSuppo rt.setServlet(DispatchActionSupport.java:78) at org.apache.struts.action.RequestProcessor.processA ctionCreate(RequestProcessor.java:297) at org.apache.struts.action.RequestProcessor.process( RequestProcessor.java:220) at org.apache.struts.action.ActionServlet.process(Act ionServlet.java:1164) at org.apache.struts.action.ActionServlet.doPost(Acti onServlet.java:415) at servletunit.struts.MockStrutsTestCase.actionPerfor m(MockStrutsTestCase.java:394) at test.struts.action.StrutsLogonTest.testExecuteActi onMappingActionFormHttpServletRequestHttpServletRe sponse(StrutsLogonTest.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknow n Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Un known Source) at java.lang.reflect.Method.invoke(Unknown Source) at junit.framework.TestCase.runTest(TestCase.java:154 ) at junit.framework.TestCase.runBare(TestCase.java:127 ) at junit.framework.TestResult$1.protect(TestResult.ja va:106) at junit.framework.TestResult.runProtected(TestResult .java:124) at junit.framework.TestResult.run(TestResult.java:109 ) at junit.framework.TestCase.run(TestCase.java:118) at junit.framework.TestSuite.runTest(TestSuite.java:2 08) at junit.framework.TestSuite.run(TestSuite.java:203) at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.runTests(RemoteTestRunner.java:421) at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.run(RemoteTestRunner.java:305) at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.main(RemoteTestRunner.java:186)
但是我在web.xml里确实是已经registe the spring ContextLoaderListener了。
郁闷了半天。
这里不得不说一下StrutsTestCase的运行机制了。
StrutsTestCase for JUnit是Junit TestCase类的扩展,提供基于Struts框架的代码测试装置。StrutsTestCase同时提供Mock 对象方法和Cactus方法用来实际运行Struts ActionServlet,你既可以通过运行servlet引擎来测试,也可以不通过它。因为StrutsTestCase使用ActionServlet控制器来测试你的代码,因此你不仅可以测试Action对象的实现,而且可以测试mappings,from beans以及forwards声明。
使用StrutsTestCase不启动servlet容器来测试struts应用程序(容器外测试)也属于Mock对象测试,但是与EasyMock不同的是,EasyMock是提供了创建Mock对象的API,而StrutsTest则是专门负责测试Struts应用程序的Mock对象测试框架。
由于我们这里是通过spring来管理struts的,所以我们这里所要面临的一个主要的问题就是要提供一个ApplicationContext为TestCase调用,这样我们引用了一个JUnitHelper工具类进行相关Spring的操作,如提供ApplicationContext环境,下面我们就实现类测试和Action类测试进行一下讲解,其中数据库连接由spring控制。由于每一个测试类中都要用到所提供的ApplicationContext环境。所以我专门为此写了一个辅助的类JUnitHelper用来取ApplicationContext环境.在MockStrutsTestCase的setup方法中,我们进行绑定操作: JUnitHelper.setWebApplicationContext(getActionServlet().getServletContext()) ; 就可以了。
具体JUnithelper.java类的代码如下:
package com.ninetowns.mes.action; import java.io.File; import java.io.IOException; import javax.servlet.ServletContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class JUnitHelper { public static String BASE_DIRECTORY = ""; public static String[] Configuration_Location; private static XmlWebApplicationContext wac = null; private static FileSystemXmlApplicationContext fsxac = null; static { initPath(); } public static void setWebApplicationContext(ServletContext context) { if (context .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) return; if (wac == null) { wac = new XmlWebApplicationContext(); wac.setServletContext(context); wac.setConfigLocations(Configuration_Location); wac.refresh(); } context.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); } public static ApplicationContext getApplicationContext() { if (fsxac == null) { fsxac = new FileSystemXmlApplicationContext(Configuration_Location); } return fsxac; } public static void initPath() { try { BASE_DIRECTORY = new File(".").getCanonicalPath(); Configuration_Location = new String[] { BASE_DIRECTORY + "/src/main/resources/applicationContext-actions.xml", BASE_DIRECTORY + "/src/main/resources/applicationContext-beans.xml", BASE_DIRECTORY + "/src/main/resources/applicationContext-common.xml" }; } catch (IOException e) { e.printStackTrace(); } } }
测试类如下:
package com.ninetowns.mes.action; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import junit.framework.Assert; import org.springframework.web.context.support.XmlWebApplicationContext; import com.ninetowns.mes.manager.tqs.TQSConfigurationLoader; import com.ninetowns.mes.model.tqs.TQSItem; import com.ninetowns.mes.util.MesMap; import servletunit.struts.MockStrutsTestCase; public class TestModifyAction extends MockStrutsTestCase { public void setUp() throws Exception { super.setUp(); setContextDirectory(new File("src/main/webapp")); JUnitHelper.setWebApplicationContext(getActionServlet() .getServletContext()); setInitParameter("validating", "false"); } public void tearDown() throws Exception { super.tearDown(); } public void testModifyAction() throws Exception { setRequestPathInfo("/add"); addRequestParameter("method", "edit"); actionPerform(); verifyForward("addpage"); } }
setContextDirectory(new File("src/main/webapp"));
这个是在src/main/webapp下查找struts-config.xml(因为我用的是maven构建的项目,所以struts配置文件在那)
JUnitHelper.setWebApplicationContext(getActionServlet().getServletContext());
这就上面说的获得Spring 的ApplicationContext。
还有一点我的Action是用的是DispatchAction,故而要加上
addRequestParameter("method", "edit");
把method也当作一个参数传入。
至于之后的测试方法的具体内容就很简单了,网上很多,千篇一律的。