单元测试虽不强求,但你一旦养成习惯,你会爱上它。另外我们保证代码质量的两个很重要的手段,一个是头(单元测试)一个是尾(codereview)。那么我们最常用的单元测试就是通过junit来进行,spring-test框架很好的集成了junit来进行这项工作,比如测试dao,测试service(参见另外一篇文章)。
同时我们还会有这样的需求我不想启动tomcat来测试action(struts)或者controller(springmvc)。好了,我们还是用咱们熟悉的工具spring-test。
先介绍如何在struts环境中,不启动tomcat来测试action请求,我们测试struts的action需要用到struts2-junit-plugin这个插件。
一、 准备工作
引入下面两个maven坐标第一个是junit的struts插件,第二个是作为mock使用(模拟对象的行为),第三个spring-test。
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<version>2.3.29</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
二、 创建一系列用到的测试文件
全部放入test文件目录(标准,所有测试文件都要归并在test文件夹下面)
整体描述,采用源代码+注释的说明
ActionTest.java文件测试action的主类
package com.jd.pop.odp.web.action;
import com.jd.pop.odp.service.TestService;
import com.opensymphony.xwork2.ActionProxy;
import org.apache.struts2.StrutsSpringJUnit4TestCase;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Created by wangxindong on 2016/11/21.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath*:/spring/spring-config-test-service.xml"})
/*这里只需要加载使用到的文件,不需要测试的文件不要加载(注意),我们现在是测试action,
由于struts启动会默认加载三个文件,分别是struts-default.xml,struts-plugin.xml,struts.xml
如果我们要单独对某一个struts文件做测试,在struts.xml文件中单独引用即可,比如<includefile="struts/struts-test.xml"/>*/
public class ActionTest extendsStrutsSpringJUnit4TestCase<VenderTestAction> {
private TestService testService;
@Before
public void setUp() throws Exception{
super.setUp();//必须要有,初始化用,参见UML图
//模拟request,response
request = newMockHttpServletRequest();
request.setCharacterEncoding("UTF-8");
response = newMockHttpServletResponse();
//创建mock对象
testService = EasyMock.createMock(TestService.class);
}
@Test
public void testQuery() throws Exception{
ActionProxy proxy = null;
VenderTestAction venderTestAction =null;
request.setParameter("oplogId", "1234567");
proxy =getActionProxy("/vendertest/oplog_query.action");
venderTestAction =(VenderTestAction)proxy.getAction();
//模拟对象的行为
//记录mock对象期望的行为
EasyMock.expect(testService.queryResult()).andReturn("test-mock");//我们期望返回test-mock,则会切断正常的调用
//进入replay阶段
EasyMock.replay(testService);
venderTestAction.setTestService(testService);//这里必须要用mock之后的对象,才能达到mock模拟对象行为的作用,比如我们这里让queryResult这个方法返回”test-mock”
String result = proxy.execute();//必须要有,否则被测试的action里面的request值为null
// String queryResult = test.query();
}
}
VenderTestAction.java被测试的action
package com.jd.pop.odp.web.action;
import com.jd.pop.odp.service.TestService;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created by wangxindong on 2016/11/21.
*/
public class VenderTestAction extends ActionSupport implementsServletRequestAware, ServletResponseAware {
private HttpServletRequest request;
private HttpServletResponse response;
@Resource
private TestService testService;
public String query(){
System.out.println("comehere venderTestAction :"+request.getParameter("oplogId"));
System.out.println("comehere venderTestAction2");
String result =testService.queryResult();
System.out.println("result:"+result);
return SUCCESS;
}
@Override
public voidsetServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public voidsetServletResponse(HttpServletResponse response) {
this.response = response;
}
public voidsetTestService(TestService testService) {
this.testService = testService;
}
}
struts-test.xml文件
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache SoftwareFoundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<packagename="vendertest" extends="struts-default"namespace="/vendertest">
<actionname="oplog_*" method="{1}"class="com.jd.pop.odp.web.action.VenderTestAction">
<resultname="success">/WEB-INF/vm/oplog/vender/logList.vm</result>
</action>
</package>
</struts>
spring-config-test-service.xml业务bean的配置文件(当然你也可以用注解的方式加入到容器环境中)
<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="testService"class="com.jd.pop.odp.service.impl.TestServiceImpl"/>
</beans>
TestService.java业务类接口
package com.jd.pop.odp.service;
/**
* Created by wangxindong on 2016/11/23.
*/
public interface TestService {
public String queryResult();
}
TestServiceImpl.java业务类实现
package com.jd.pop.odp.service.impl;
import com.jd.pop.odp.service.TestService;
/**
* Created by wangxindong on 2016/11/23.
*/
public class TestServiceImpl implements TestService {
@Override
public String queryResult() {
return "test-reslut";//注意这里返回的是 test-result这个值,我们在假设这个依赖不//好使的时候我们在mock的时候,是返回的另外一个值
}
}
struts.xml文件
<?xml version="1.0"encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache SoftwareFoundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constantname="struts.enable.DynamicMethodInvocation"value="false"/>
<constantname="struts.devMode" value="false"/>
<constantname="struts.objectFactory" value="spring"/>
<include file="struts/struts-test.xml"/>
</struts>
三、逐一说明具体用法
1、struts启动的时候会默认加载struts-default.xml,struts-plugin.xml,struts.xml这三个文件,因此如果如果我们要单独对某一个struts文件做测试,在struts.xml文件中单独引用即可,比如<includefile="struts/struts-test.xml"/>
2、super.setUp();//必须要有,初始化用,参见UML图,它是用来初始化比如容器,准备文件一系列工作
3、Mock的使用,我们调用第三方的接口,比如jsf等,如果很慢,或者他们压根测试环境就坏掉了,这个时候mock就非常强大了。比如我们这里的使用,来模拟TestService的queryResult()返回情况。
(1)//创建mock对象
testService = EasyMock.createMock(TestService.class);
(2)//记录mock对象期望的行为
EasyMock.expect(testService.queryResult()).andReturn("test-mock");//我们期望返回test-mock,则会切断正常的调用
(3)//进入replay阶段
EasyMock.replay(testService);
总结:优点1-不用启动tomcat即可测试action请求,提高工作效率 2-采用mock,在第三方接口坏掉的情况下,仍然不影响我们测试(如果我们不用mock,另外一种方式就是stub(桩)打桩的方式,另外写一个实现类实现TestService,这样工作量就会比较大,mock这种模拟对象行为的方式非常优秀)