刚进公司,写两个星期的单元测试。公司主要用Jmockit ,感觉用起来还不错,于是乎,把一些写单测的时候,常遇到的写法写了下来。
凡是单元测试,都需要做以下几件事情
1, 明确要测试的类,和方法
2, 明确测试的实现,需要哪些依赖
3, 针对这些外部依赖 进行 mock
4, 验证是否根据自己的输入取得自己的输出
那么在JMOCKIT中。
做到1 这点,使用@Tested这个 注解
做到2这点,使用@injected 这个注解
做到3这点,可以使用 Expectations 和 NonStrictExpectations
做到4这点,则可以使用junit 的Assert 或者 Verifications
那么常见的写法就贴个出来:
@Tested @Mocked(methods = "updateEditNum") private CustomerServiceImpl customerServiceImpl; @Injectable private CustomerDao customerDao; @Injectable private CustOptionalDao custOptionalDao; @Injectable private CustAccountInfoDao custAccountInfoDao; @Injectable private CustContactPhoneDao custContactPhoneDao; @Injectable private CustContactDao custContactDao; @Injectable private CustRecipDao custRecipDao; @Injectable private CustAutoAuditInfoDao custAutoAuditInfoDao; // 需要修改monogo数据 @Test public void testUpdateCustomer_need_update_monogo() { CustomerVO customerVO = buildCustomer(); nonstrictExpectations(); customerServiceImpl.updateCustomer(customerVO); new Verifications() { { mongoUpdateService.modSaleData(anyLong, anyString); times = 1; mongoCoreWordFacade.modCoreWordData(anyLong, anyString, anyLong); times = 1; } }; } private void nonstrictExpectations() { new NonStrictExpectations() { { customerServiceImpl.updateEditNum((CustomerVO) any, (Map<String, Short>) any); result = null; customerDao.findById(anyLong); Customer customer = new Customer(); customer.setFullName("unit test fullname change"); customer.setStat1(StatMachineService.STAT1_IN_FOLLOWING_04); result = customer; custContactDao.saveOrUpdate((CustContact) any); CustContact c = new CustContact(); c.setContactId(11l); result = c; custContactPhoneDao.saveOrUpdate((CustContactPhone) any); CustContactPhone p = new CustContactPhone(); p.setPhoneId(22l); result = p; } }; }
最常用的有了,但单元测试可不是那么简单。我这边碰到第一个情景是,测试一个service.doXXX() 。
但这个 doXXX()其实里面就调用了2个private方法,那么其实问题就转变成分别测试这两个private 方法:
主要使用的是:Deencapsulation
// 测试私有方法获取该类型的para数据字典 (不传 单位岗位ID) @Test public void testFindByTypeAndPosIdWithCache_NO_POS() { new NonStrictExpectations() { { paraDao.findByTypeAndPosid(ParaTypeEnum.auditAbandReason); List<Para> pararet = new ArrayList<Para>(); Para p = new Para(); p.setDelFlag((short) 0); p.setShare((short) 0); p.setName("这个客户不是火星来的么,所以不能审核通过的!"); pararet.add(p); returns(pararet); } }; List<Para> rets = Deencapsulation.invoke(paramServiceImpl, "findByTypeAndPosIdWithCache", ParaTypeEnum.auditAbandReason, new Long[] {}); Assert.assertTrue(rets.get(0).getName().equals("这个客户不是火星来的么,所以不能审核通过的!")); new Verifications() { { calloutMemcachedClient.get(anyString); times = 1; calloutMemcachedClient.set(anyString, anyInt, anyString); times = 1; } }; }
但是查看源码 发现 继承于 Invocations的 Expectations 和 Verifications 也都是有invoke 的反射方法已经提供好了。
第二种情况来了,我要测试的@Tested 类,里面本身也有需要mock 的方法,但只 mock 其中一个方法就够了
@Tested @Mocked(methods = "updateEditNum") private CustomerServiceImpl customerServiceImpl;
第三种情况来了,有个实现的地方需要mock ,但这个实现是这样的 A.doAA().doBB().doCC()
难道要先mock A.doAA(),然后在mock这个返回值的doBB() 。。。。 太麻烦了
来看下:
// 测试用户信息比较全,并且需要优先审核,会更新优先统计数 @Test public void testAddCustomer(@Cascading final ServiceLocator ServiceLocator) { long custId = 38888888l; CustomerVO vo = new CustomerVO(); vo.setFullName("单元测试用户"); vo.setCustId(custId); 。。。。。 new NonStrictExpectations() { { ServiceLocator.getInstance().getBean("saleSetFacade"); saleSetFacade = new SaleSetFacade() { public SaleSet findSaleSetById(Long id) { return null; } 。。。。。。 }; result = saleSetFacade; } };