原来的façade代码
public class XxFacadeImpl implements XxFacade { public List doSomeThing(String param1) { //1组装查询条件 XXVO xxVO=new XXVO(); xxVO.setParam(param); //2.通过条件查询数据库已获得相业务数据 List xxList = xxDao.getXXList(xxVO); //Dao逻辑1 //3.基于xxList的业务计算 XXVO xxVO1 = new XXVO(); //业务逻辑1 For(int i=0;i<xxList.size();i++){ xxVO1 = (XXVO) xxList.get(i); xxVO1.setName(String.valueOf(i)); } //4.把计算结果更新到数据库 xxDao.updateXXList(xxList); //Dao逻辑 //5.做其他事 //业务逻辑 2 return xxList; } }
这样的代码很常见,但是这样把业务代码和持久层方法调用写在一起的方法却很难被测试,如果想测试这样的方法大概的流程如下
1. 在数据库中准备符合条件的数据(insert)符合param1的查询条件
2. 调用XxFacadeImpl的doSomeThing(1)方法
3. 得到返回的List 验证业务逻辑1是否正确
对于这样的代码可以勉强测试,但是却相当麻烦,首先要建立测试数据,要想着自己所查询的表是否有关联,如果有还要一同插入测试数据,否则Dao逻辑1将不能得到数据,导致测试失败,测试之后还需要把数据回滚,不然数据库中就会留下垃圾数据。
很明显我们的façade并不关心Dao的实现或者表中的数据,但为了测试这个方法却要模拟这样一个环境。然而对于业务逻辑2中的代码我们如何测试呢?这只是一个勉强可以测试的Façade方法因为他所操作的数据正好是这个方法的返回值,我们简单修改一下这段代码
public class XxFacadeImpl implements XxFacade { public boolean doSomeThing(String param1) { //1组装查询条件 XXVO xxVO=new XXVO(); xxVO.setParam(param); //2.通过条件查询数据库已获得相业务数据 List xxList = xxDao.getXXList(xxVO); //Dao逻辑1 //3.基于xxList的业务计算 XXVO xxVO1 = new XXVO(); //业务逻辑1 For(int i=0;i<xxList.size();i++){ xxVO1 = (XXVO) xxList.get(i); xxVO1.setName(String.valueOf(i)); } //4.把计算结果更新到数据库 //upadateXXList(xxList)将返回更新是否成功 return xxDao.updateXXList(xxList); } }
只是简单的修改了一下方法的返回值,我们现在只关心这个update 方法是否成功.
那怎么才能测试业务逻辑1呢?
总结一下,
不可测试方法:这样的Façade很难被测试,甚至有些部分不能被测试或方法中的逻辑根本不能被测试,
垃圾数据:会在数据库中留下大笔的垃圾数据,虽然可以通过继承TestDaoConstant来自动回滚,但Façade业务本身是不需要事务的,如果引入mock来做孤立测试,Mock带来的复杂度也将被引入。
代码复用:这样的代码通常很难被复用。
测试覆盖度:由于方法中有不可测试部分也就不能保证测试的覆盖度。
改良的Façade编码如下
public class XxFacadeImpl implements XxFacade { public boolean doSomeThing(String param1) { //1组装查询条件 XXVO xxVO=new XXVO(); xxVO.setParam(param); //2.通过条件查询数据库已获得相业务数据 List xxList = xxDao.getXXList(xxVO); //Dao逻辑1 //3.基于xxList的业务计算 editUserName(xxList); //业务逻辑1 //4.把计算结果更新到数据库 xxDao.updateXXList(xxList); //Dao逻辑 //5.做其他事 //业务逻辑 2 someBusiness2(xx); return true; } public List editUserName(List xxList){ XXVO xxVO1 = new XXVO(); //业务逻辑1 for(int i=0;i<xxList.size();i++){ xxVO1 = (XXVO) xxList.get(i); xxVO1.setName(String.valueOf(i)); } return xxList; } public List someBusiness2(xxx){ ... … … … return result; } }
这样的代码XxFacade中doSomeThing方法并不关心业务逻辑的具体实现,只是这个逻辑的实现流程,他只是调用一些纯业务方法,而这些纯业务方法又不需要依赖Dao数据我们随便组装一个List就可以测试,我们不再需要数据库,也摆脱了事务,需要测试的只是这些干干净净的纯业务方法,他们只依赖简单的参数,返回这个业务操作后的数据。至于XxFacade中doSomeThing方法我们甚至不需要去测试他,他所调用的方法全部是测试过的可信赖方法
看看简单的改动为我们带来了什么?
1. 使方法可测试,提高测试覆盖度
2. 让Façade可以脱离事务,数据库进行测试
3. 有效的拆分方法,每个方法的业务都很简单,一屏足已装下
4. 保证每个纯业务方法都只做一件事。
5. 提高方法被复用的机会
由于方法doSomething中所调用的全部是可信任方法,也就直接提高代码的质量和覆盖度,增加了重构的勇气。
最后希望大家可以多多提交有效的测试程序,提高我们代码的质量,下班前跑遍测试看到著名的green bar回家睡个安稳觉。