可以看到为了测试handle()方法,我们必须mock2个对象(fooDao和barService),对于实际的代码,我们极有可能需要mock4-6个对象,这样单测一个方法的成本太高了。

  站在职责单一原则的角度思考,对于FooServiceImpl中的handle()中的两步完全可以分开,因为调用barService明显超出了它的职责,service调用service时我们需要思考下是否可以放在fa?ade里面,这样层次明晰后代码职责更清晰了,我们重构成两个类:FooServiceImpl和FooFacadeImpl:

  可以看到FooFacadeImpl中handle()实现了替换功能,而FooServiceImpl中只用到了FooDao一个依赖对象了,那么在测试preHandle只用构造一个mock对象了。

  这时候有人发问:这时测试FooFacadeImpl中的handle()需要构造两个测试对象了???测试还是没有变简单,这样只是把mock对象的时间延后了。其实handle()中的替换操作应该封装在BarService中,FooFacadeImpl中只是调用这个两个service:

  这样预处理操作封装在FooService中,替换操作封装在BarService中,分别对这两个类中的方法进行测试分别只用mock一个对象,一方面增加了测试的信心(书写成本低、迅速调通可运行的测试case),另一方面代码也变得更有层次感和清晰。回到问题上来,怎么测试FooFacadeImpl,是否还需mock两个对象并准备数据?其实当我们充分测试preHandle()和postHandle()方法后,FooFacadeImpl只用测试使用各调用了preHandle()和postHandle()一次:

  从这个例子可以看出,对service进行单测成本高的时候极有可能是代码构成不合理,对于一个service中有多个service和dao的情况,可以建立业务逻辑的层次调用,避免出现大类的情况,这样对于降低单测成本、做到低耦合是大有帮助的。