您的位置:首页 > 编程语言 > Java开发

开发中的单元测试

2016-03-17 00:00 351 查看
摘要: 在日常开发中我相信我们都会做少量的单元测试, 在大企业会有专业的测试团队编写好测试驱动, 让开发人员编写的每一个方法都能通过测试驱动. 但是在中小型企业, 这些都需要我们自己去实现.

我是个测试专业的Java开发, 最近做毕业设计时总结的心得, 在这分享给大家.

由于对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中.

@Autowired
HttpSession session;
@Autowired
ServletContext context;

这样用起来虽然简单, 但是会给单元测试造成困难. 原因: session和servletContext都是在服务器启动时创建, 而单元测试并没有去启动服务器所以Spring是没有办法去注入这两个东西的. 而且(个人经验)这种写法并不符合常规,我们可以通过Controller层去获取然后传入到Service层中, 我暂时还没有找到在单元测试注入到Spring容器的方法. (由于上面用的注解注入, 用setter并不能解决问题, 去掉注解则会造成代码污染.) 如果一定要用到的~只要不使用注解的方法去注入,,然后加上setter都可以让spring成功初始化,在测试用到时通过setter注入就可以.

Service层.

在工作的时候,我也只是用SpringTest初始化容器,然后调用一下Service的方法这样去测试,但是这样的测试当遇到错误时,我们还需要去考虑到底是Service的错还是Dao层的问题.而且Spring去初始化用时非常长,花这么多时间去做一个单元测试并不是太合理. 这里我们可以使用EasyMock去构造一个Dao层方法的Mock对象, 录制所需要的执行的方法并在Service层中回放.下面我大概说说用法.

private BaseDAO<Admin> adminBaseDao;
//需要通过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方法

@TestedObject
private AdminService adminService;

@Mock
@InjectInto(target = "adminService", property = "adminBaseDao")
private BaseDAO<Admin> adminBaseDao;

这样去测试Service层的好处是, Dao层的方法都是自己模拟出来的, 如果Service操作指定的数据是正常的那么当出现问题的时候就知道应该是Dao层的问题. 而且跳过了Spring初始化, 那个速度真的快了超多.

对于Controller层

使用Struts2的小伙伴可以使用Struts2的 Struts2Test的模块

public class AdminActionTest extends StrutsSpringJUnit4TestCase<AdminAction>

注意:@Before的方法名不能用setUp. 根据JUnit3 初始化方法会使用setUp作为方法名,但是在这里Struts2Test在源码里面已经有这个setUp方法, 你在写一个会覆盖他的方法, 导致request和response无法mock出来.

贴段Struts2Test源代码:

@Before
public 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();

@Before
public void setup() {
awardControler = new AwardControler();
awardService = EasyMock.createMock(AwardService.class);
awardControler.setAwardService(awardService);
}

@Test
public 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
Map<String, Object> expected = new HashMap<String, Object>();
expected.put("id", "1");
expected.put("Id", "1");
expected.put("status", "ok");
ModelAndViewAssert.assertViewName(modelAndView, "souche/award/detail.jsp");
ModelAndViewAssert.assertModelAttributeValues(modelAndView, expected);
}


PS: 单元测试的核心是隔离代码, 所以很多地方用到了Mock技术, Mock的方法有很多 JMock, EasyMock甚至自己去建一个Mock对象, 本文没有详尽去介绍每一句代码的意思, 本意只是为了分享下单元测试的方法或者一些可能用到的技术, 读者可以根据本文去搜索相关资料, 如果有小弟哪里写错的请亲们在评论中指正, 大神们你们有权去说这是垃圾文章, 不过我希望大神们能够指出小弟的不足, 让我去好好学习相关的知识提升自己, 希望大家做个负责任的读者, 如果想交个朋友的我QQ是41369927 小弟非常乐意广交朋友..讨论技术,讨论理想,讨论生活. 测试的毕业设计真不好做, 不过学到的知识始终的自己.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JUnit Unitils Java Mock Spring