设为首页 加入收藏

TOP

使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测(五)
2018-03-08 09:05:46 】 浏览:873
Tags:使用 Java 函数 接口 lambda 表达式 隔离 模拟 外部 依赖 容易 单测
GetData(JSONObject jsonObject) { if (i == 0) { return new ArrayList<>(); } i--; return jsonObject.getJSONArray("list").toJavaList(Integer.class); }

外部依赖引入源

综上例子,一个方法的外部依赖引入源主要有:

(1) 方法所在类的实例变量,在方法里引用就如同引用了可能被随时修改的全局变量,是非常破坏方法的纯粹性的;

(2) 方法所在类注入的Service, 在方法里使用就成了方法的外部依赖,往往要写Mock外部依赖的结果数据才能进行单测;

(3) 方法调用了依赖外部服务的下层方法,导致方法有间接依赖。

对于(1),含有业务逻辑的方法应当将实例变量作为函数参数; 对于 (2) 和 (3), 使用函数接口和lambda表达式隔离和模拟依赖服务。

不过这里有两个问题:

(1) 如果一个方法依赖了多个 service 或 多个方法,怎么办? 那就要传入多个 Function 参数了。 另一种办法是,遵循单一职责原则,尽量编写短小的只含有至多一个Service或方法依赖的方法。每个方法只做明确的一件事。 很多调用多个Service 或多个方法的方法,就是做了太多事情了,每件事都不彻底,导致每次扩展都要在一个方法里增加很多条件分支。

(2) 大量的函数接口和lambda表达式可能像回调一样,容易将人绕晕。因此,一个函数最多两个函数接口为宜。 而函数接口和lambda表达式的使用,需要整体策略来控制,保持工程的可理解性和可维护性。 毕竟,可测性只是工程质量的一个属性,不能过于追求一个属性而破坏其他属性。

工程的“版图”

一个工程里应当被划分为“两半版图”:版图A是依赖于各种外部服务的调用,版图B是不依赖于任何外部服务的独立业务方法和工具类。版图B中的独立业务方法充满着各种业务逻辑和判断,是容易编写单测的,而版图A是没有必要写单测的,因为里面没有逻辑。这样,我们将工程中的外部依赖“驱逐到”版图A,类似于第九区里的“外星人管理区”。

理想情况下,版图B应该是占90%的领土,版图A应该占10%的领土。不过,实际工程中正好相反,版图A占了90%的领土,版图B却被驱逐到util包下,只占10%,单测还往往被忽视。 怎么改造呢? 实际上也很简单: 一旦从A的业务方法 FA 中发现外部依赖,就抽离出一个独立方法 FB 来隔离外部依赖,放到版图B里,然后对 FB 进行仔细单测,而 FA 只作为一个壳或外观模式,通过联调来确保正确。

对外部依赖的隔离,使得更容易编写单测,更容易获得更高的单测覆盖率和单测质量。

此外,导致单测编写困难的另一个“罪魁祸首”,就是不好的编程习惯,将大量多个逻辑放在同一个方法里。这样,为了测试一个东西,要构造大量的对象;同时,对其中的子部分则不容易测试彻底,导致隐藏的BUG。

对于增强代码可测性的唯一建议就是: 拆解、隔离。

单测策略

并不是所有代码都需要写单测的。也不是所有代码用单测更有效率。 在我看来,如果是纯顺序的逻辑,可以通过接口测试来保证,尤其是对于那些依赖外部服务的单行调用,既无法写单测也不必要写单测。而对于具有条件分支、循环分支等的逻辑,则要尽可能隔离成独立方法或函数,从而更容易滴更有效率地单测。

单测并不需要100%的覆盖率,也不应当花费过度的成本去追求高的覆盖率。 100%的覆盖率也不代表质量杠杠滴。 在单测覆盖率和软件开发成本中,必须有一个平衡。更好的软件质量,应当是较高的单测覆盖率与适当的接口用例覆盖的双重护航而保障,而不是把注都押在单测上。

疑虑

当然,使用任何一种新方式,总会有疑虑的。

高阶函数不易掌握

使用函数接口,或者说高阶函数的写法,对于很多童鞋可能还很不适应。 不过,这种写法以后很可能会成为主流。 因为它便捷、安全,而且很容易产生通用化的方法。通过高阶框架函数以及许多自定义业务函数的反复组合,构建起整个软件。

事实上,高阶函数并不陌生。在 C 语言时代,就已经通过函数指针支持传入函数参数了。 因此,高阶函数,只是将函数指针“对象化”了,并不是新鲜玩意。

多出的方法

从上面的例子可以看到,每一个被改造的方法,最终会得到两个方法: 一个隔离了外部依赖的独立函数,一个依赖外部服务的单行调用。独立函数便于测试,而单行调用通常通过联调来保证OK。这对软件测试是个福音,不过对于程序员来说,会不会是额外的负担呢?可能取决于各自的选择吧。至少在我看来,多一个方法,却能够更方便地测试,甩掉繁重的mock单测框架,是非常值得的。此外,通常还能从中挖掘出更通用的方法,消除重复的业务代码,也是另一个好消息。

工程隐患

在生产环境的工程中大量使用函数接口和lambda表达式,是否有隐患呢?目前还没有确切证据。如果有了,可以不断积累经验,但不应当因噎废食。一种新技术、新方式,总要踩上若干坑,才能成为成熟的技术,将软件开发推向一个新的里程碑。

在我所负责的订单导出工程里,已经大量使用了函数接口和lambda表达式。如果运行不稳定,那么也可以得到第一手的资料。且让我们拭目以待。

自动生成单测

一旦我们尽可能将依赖外部服务的函数转化为“非依赖于外部服务的独立函数+外部服务的单行调用”,编写单测的工作就变成了对独立函数的单测。而独立函数的单测是可以自动生成的。后续会专门有一篇文章来谈到Java单测类模板的自动生成。目前仅仅谈及思路。

单测的编写模板无非是:解析方法签名; 创建对象; 设置对象值; 设置外部服务返回数据; 检测返回结果。 解析方法签名通过可以使用正则表达式;创建对象和设置对象属性,可使用java反射机制; 设置外部服务返回数据, 可创建简单的 lambda 表达式来模拟。

首页 上一页 2 3 4 5 下一页 尾页 5/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇深入Spring Boot: 怎样排查 java... 下一篇如何编写相对标准的后端项目(二..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目