本文共 4076 字,大约阅读时间需要 13 分钟。
由于对Dao层的测试时间成本相对来说比较大,而Dao层本来就没有复杂的逻辑所以在这选了Service和Controller层的单元测试.
基于Spring的单元测试库Spring-Test
这对于在用Spring的亲们应该不陌生, SpringTest可以在单元测试中初始化Spring的上下文, 这一点就已经相当方便了.
通过
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath*:applicationContext.xml")
这两个注解就可以实现, Spring的配置文件应该放在classpath中. Maven的就放在test文件夹中的resources文件夹中.
注意:
我曾经遇到过有朋友会将HttpSesson或者ServletContext注入到Service中.
@AutowiredHttpSession session;@AutowiredServletContext context;
这样用起来虽然简单, 但是会给单元测试造成困难. 原因: session和servletContext都是在服务器启动时创建, 而单元测试并没有去启动服务器所以Spring是没有办法去注入这两个东西的. 而且(个人经验)这种写法并不符合常规,我们可以通过Controller层去获取然后传入到Service层中, 我暂时还没有找到在单元测试注入到Spring容器的方法. (由于上面用的注解注入, 用setter并不能解决问题, 去掉注解则会造成代码污染.) 如果一定要用到的~只要不使用注解的方法去注入,然后加上setter都可以让spring成功初始化,在测试用到时通过setter注入就可以.
Service层.
在工作的时候,我也只是用SpringTest初始化容器,然后调用一下Service的方法这样去测试,但是这样的测试当遇到错误时,我们还需要去考虑到底是Service的错还是Dao层的问题.而且Spring去初始化用时非常长,花这么多时间去做一个单元测试并不是太合理. 这里我们可以使用EasyMock去构造一个Dao层方法的Mock对象, 录制所需要的执行的方法并在Service层中回放.下面我大概说说用法.
private BaseDAOadminBaseDao;//需要通过setter方法注入到Service中//在测试方法中EasyMock.createMock(BaseDAO.class);EasyMock.expect(adminBaseDao.get("from Admin a where a.phone=? and a.password=?", new Object[]{ "110","123"})).andReturn(result);EasyMock.replay(adminBaseDao);
测试Service层的时候其实已经没有必要去初始化Spring了,
至于注入到Service 方法1:可以直接new一个~然后通过setter将用到的mock出来的Dao注入进去.
方法2:使用Unitils中的Inject模块使用@InjectInto注解就可以不用添加setter方法
@TestedObjectprivate AdminService adminService;@Mock@InjectInto(target = "adminService", property = "adminBaseDao")private BaseDAOadminBaseDao;
这样去测试Service层的好处是, Dao层的方法都是自己模拟出来的, 如果Service操作指定的数据是正常的那么当出现问题的时候就知道应该是Dao层的问题. 而且跳过了Spring初始化, 那个速度真的快了超多.
对于Controller层
使用Struts2的小伙伴可以使用Struts2的 Struts2Test的模块
public class AdminActionTest extends StrutsSpringJUnit4TestCase
注意:@Before的方法名不能用setUp. 根据JUnit3 初始化方法会使用setUp作为方法名,但是在这里Struts2Test在源码里面已经有这个setUp方法, 你在写一个会覆盖他的方法, 导致request和response无法mock出来.贴段Struts2Test源代码:
@Beforepublic void setUp() throws Exception { super.setUp(); this.initServletMockObjects(); this.setupBeforeInitDispatcher(); this.initDispatcherParams(); this.initDispatcher(this.dispatcherInitParams);}
在测试时按上面EasyMock的方法将Service Mock出来, 然后再通过setter或者Unitils inject 注入到Action里面, 然后需要用ActionProxy来注入…(在里面有一个executeAction的方法可以直接执行Action,如果是通过初始化Spring来测试的就可以使用,但是这样没办法注入MockService), 因为ActionProxy并不是通过request来获取参数的, 所以参数用到的对象也应该用setter或者inject 注入到Action中.
ActionProxy proxy = getActionProxy("/admin/login.action");AdminAction action = ((AdminAction) proxy.getAction()).setAdminService(adminService);action.setAdmin(admin);action.setSession(request.getSession());String result = proxy.execute();Assert.assertEquals("success", result);
如果是使用SpringMVC的朋友其实道理都是一样的.都是Mock这个注入进去, Mock那个注入进去, SpringMVC的,mock request和mock response需要自己去创建, 直接上代码了;
private MockHttpServletRequest request = new MockHttpServletRequest();private MockHttpServletResponse response = new MockHttpServletResponse();@Beforepublic void setup() { awardControler = new AwardControler(); awardService = EasyMock.createMock(AwardService.class); awardControler.setAwardService(awardService);}@Testpublic void testGetAwardDetail() throws Exception { //create mock return object Award award = new Award(); award.setId("1"); award.setName("测试1"); award.setStatus("未发放"); request.setRequestURI("/awardController/getAwardDetail.do"); request.setMethod(HttpMethod.POST.name()); request.setParameter("id", "1"); EasyMock.expect(awardService.getAwardById("1")).andReturn(award); EasyMock.expect(awardService.getAwardStatusJSON()).andReturn("ok"); EasyMock.replay(awardService); ModelAndView modelAndView = awardControler.getAwardDetail(request, response); //create expect model Mapexpected = new HashMap (); expected.put("id", "1"); expected.put("Id", "1"); expected.put("status", "ok"); ModelAndViewAssert.assertViewName(modelAndView, "souche/award/detail.jsp"); ModelAndViewAssert.assertModelAttributeValues(modelAndView, expected);}
软件测试交流群,加群qq(**644956177)**群里有技术交流和资源分享。
转载地址:http://kghf.baihongyu.com/