Spring Test + Struts2 Convention 控制层单元测试
第一次写博客,记录下今天学习Spring Test时遇到的问题。
一、问题描述
下面是我的控制层测试代码:
@RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({}) public class LoginActionTest extends StrutsSpringTestCase { /** * 默认路径 * classpath*:applicationContext.xml * @throws Exception */ @Override protected String[] getContextLocations() { return new String[]{"classpath:applicationContext.xml"}; } @Before public void setUp() throws Exception { //1 指定Struts2配置文件 //该方式等价于通过web.xml中的<init-param>方式指定参数 Map<String, String> dispatcherInitParams = new HashMap<String, String>(); ReflectionTestUtils.setField(this, "dispatcherInitParams", dispatcherInitParams); //1.1 指定Struts配置文件位置 dispatcherInitParams.put("config", "struts-default.xml,struts-plugin.xml,struts.xml"); super.setUp(); } /** * @throws Exception * */ @Test public void testWelcome() throws Exception { String output = executeAction("/user/login!welcome.action"); assertEquals(Action.SUCCESS, output); } @Test public void testDeal() throws Exception { request.setParameter("username", "张三"); request.setParameter("password", "1"); ActionProxy proxy = getActionProxy("/user/login!deal.action"); assertNotNull(proxy); LoginAction action = (LoginAction) proxy.getAction(); assertNotNull(action); String result = proxy.execute(); assertNull(result); // String username = (String) findValueAfterExecute("username"); // String password = (String) findValueAfterExecute("password"); // assertEquals("张三", username); // assertEquals("1", password); System.out.println(response.getContentAsString()); assertEquals("张三", action.getModel().getUsername()); assertEquals("1", action.getModel().getPassword()); } }
下面是JUnit4报错记录:
这是控制台输出的错误:
2016-01-21 15:18:09,848 WARN (org.springframework.mock.web.MockServletContext:319) - Couldn't get resource paths for class path resource [WEB-INF/jsp/user/] java.io.FileNotFoundException: class path resource [WEB-INF/jsp/user/] cannot be resolved to URL because it does not exist at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:187) at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:48) at org.springframework.mock.web.MockServletContext.getResourcePaths(MockServletContext.java:303) at org.apache.struts2.convention.DefaultResultMapBuilder.createFromResources(DefaultResultMapBuilder.java:252) at org.apache.struts2.convention.DefaultResultMapBuilder.build(DefaultResultMapBuilder.java:189) at org.apache.struts2.convention.PackageBasedActionConfigBuilder.createActionConfig(PackageBasedActionConfigBuilder.java:933) at org.apache.struts2.convention.PackageBasedActionConfigBuilder.buildConfiguration(PackageBasedActionConfigBuilder.java:702) at org.apache.struts2.convention.PackageBasedActionConfigBuilder.buildActionConfigs(PackageBasedActionConfigBuilder.java:348) at org.apache.struts2.convention.ClasspathPackageProvider.loadPackages(ClasspathPackageProvider.java:53) at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:268) at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:67) at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:445) at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:489) at org.apache.struts2.util.StrutsTestCaseHelper.initDispatcher(StrutsTestCaseHelper.java:54) at org.apache.struts2.StrutsTestCase.initDispatcher(StrutsTestCase.java:229) at org.apache.struts2.StrutsTestCase.setUp(StrutsTestCase.java:209) at web.user.LoginActionTest.setUp(LoginActionTest.java:47)
二、解决方法
参照http://blog.csdn.net/jammiwang19870815/article/details/9175607添加自己的ResourceLoader,即可解决,在写的时候注意ResourceLoader路径问题,要与真实路径一致,下面是我的代码:
public class MyResourceLoader extends DefaultResourceLoader { private static final Logger logger = LoggerFactory.getLogger(MyResourceLoader.class); @Override public Resource getResource(String location) { if(location != null && location.startsWith("/WEB-INF/")) { logger.info(location); URL path = null; try { String str = "file:/" + System.getProperty("user.dir") + "/WebRoot" + location; logger.info(str); path = new URL(str); } catch (MalformedURLException e) { e.printStackTrace(); } return new UrlResource(path); } return super.getResource(location); } }
其中评论中提到另一个解决方案:
log4j.properties中添加:log4j.logger.org.springframework.mock.web.MockServletContext=error
该方法只是屏蔽了控制台的输出,但是测试还是没有通过,所以是不行的!
三、总结
1、为何testDeal()测试方法通过,而testWelcome()测试方法没有通过呢?
其原因是:testDeal()对应的LoginAction deal()方法return为null,而testWelcome()对应的LoginAction welcome()方法return为success,会对应WEB-INF/jsp/user/login.jsp,由于它默认是查找classpath下的路径,所以找不到报错,这就是为什么要重写ResourceLoader的原因。
说明:这个是否出错与你写的Action有关,只是本例是这么写的。
2、getContextLocations默认查找的是classpath*:applicationContext.xml,所以本例中的getContextLocations()方法可以不复写。
3、若测试类不添加@RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({})注解则不能使用@Ignore忽略测试,而且默认是所有方法都进行测试,即默认添加了@Test注解,但是加了前面两个注解后,JUnit4的注解都能正常使用,目前还不明白是为何?
附录
最后再把LoginAction补齐:
@SuppressWarnings("serial") @Namespace("/user") public class LoginAction extends StrutsAction<User> { @Autowired private UserService userService; public String welcome() { logger.info("登录页面"); return SUCCESS; } /** * 登录 * @return * @throws UnsupportedEncodingException */ public String deal() throws UnsupportedEncodingException { Result result = null; boolean flag = userService.exist(model.getUsername()); if(flag) {//登录 flag = userService.exist(model); if(flag) { //保存user对象到session中 Map<String, Object> session = ActionContext.getContext().getSession(); session.put("username", model.getUsername()); String resultUrl = Struts2Utils.getRequest().getContextPath() + "/main.action"; result = new Result(Result.SUCCESS, "", resultUrl); } else { result = new Result(Result.ERROR, "密码错误", ""); } } else { result = new Result(Result.ERROR, "用户不存在", ""); } Struts2Utils.renderText(result); return null; } /** * 退出 * @return */ public String logout() { Map<String, Object> session = ActionContext.getContext().getSession(); session.remove("username"); return "logout"; } }