刚进公司,写两个星期的单元测试。公司主要用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;
}
};