| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 开发测试 -> 做个人吧,写点Testable代码,好吗! -> 正文阅读 |
|
[开发测试]做个人吧,写点Testable代码,好吗! |
标题所说的Testable实际上指的是Unit Testable,也就是可单元测试,或者是易书写单元测试的代码。
需求解析 那怎样才算是UnTestable的代码呢?又是什么原因导致很难对其书写单元测试代码呢?? 可以通过一个实际需求来做详细分析,假设我们要实现一个智能灯光控制器,控制器的功能是根据当前所处时间模式,自动将灯光打开或者关闭。其中灯光的模式根据当前系统时间分为以下几种
上述需求并不复杂,仔细拆分下需求,基本包含以下2部分:
实现代码如下:
在上述实现中,getTimeOfDay()方法起着承上启下的作用。正如注释中描述一样,它的作用就是获取当前系统时间,并返回相应主题字符串的方法。 在SmartLightController中主要有2个方法,getTimeOfDay和switchLights。他们的作用依次是返回时间模式和执行开/关灯光操作。因此我们需要对这2个方法执行单元测试,以保证方法的准确性。 问题出在哪? 首先来看getTimeOfDay方法究竟有哪些问题呢?我们可以从2个角度分析这段代码存在的问题。 1. 从单元测试的角度看 如果我们想对这个方法进行单元测试是很困难的。原因在于Calendar.getInstance().get这个接口是跟设备实际情况绑定在一起的。比如我们想测试时间点在8点的时候,getTimeOfDay是否正确的返回"Morning"字符串,那就只能等到早上8点整,准时执行这个方法;或者我们可以在系统设置里手动的将时间设置为早上八点,然后执行此方法。最终单元测试会变成如下形式:
可想而知,如果我们要对每一个时间点都进行单元测试,那会是一项极其耗时又耗力的工作。这严重违反了单元测试快速、独立的原则。 2. 从面向对象开发原则的角度 另一方面,从面向对象开发原则上来看,getTimeOfDay的实现也有待完善。首先它违反了SRP职责单一原则。从职责上来看,getTimeOfDay只需要处理当前时间,然后返回字符串即可。但是在其内部实现中,还承担了调用Calendar接口获取系统当前时间的任务。并且这也间接导致getTimeOfDay方法与设备的系统时间实现有严重的耦合。 代码重构 了解了问题的严重性之后,剩下的只需要将其完善即可。从之前的分析可以看出,似乎所有的问题都出在Calendar.getInstance().get()这行代码中,因为它是所有代码耦合的根源。因此解决办法也就是将其"解耦"。其实解耦方式很简单,我们只需要将其以参数的形式传入getTimeOfDay方法即可,如下所示:
通过传入一个int类型的参数,使得getTimeOfDay方法的唯一职责就是判断hour参数,同事不再与系统时间的接口有任何耦合。并且接下来的单元测试代码也会变得非常简单,如下:
似乎看起来,getTimeOfDay方法到目前为止已经变得Testable了,是不是就万事大吉了呢?很遗憾并没有,刚才我们将getTimeOfDay进行了重构,为其添加了hour参数,这虽然解决了它与Calendar API的耦合;但本质上是将锅甩给了它的调用者,也就是switchLights方法。因此经过修改后的完整代码如下: 很明显,问题并没有根治!如果要对switchLights方法书写单元测试方法,我们还是会遇到同样的问题。 接下来是不是需要像getTimeOfDay方法一样,继续将hour以参数的形式传入进来呢?虽然我们可以这样做,但是这种做法始终不能根治问题,只是将问题一级一级的往上抛而已。 那如何根治这个问题呢?众多解决方案中,有一种非常好的方式就是:依赖注入 Dependency Injection。 依赖注入 上文中也已经提到过,问题的根本在于对于Calendar的耦合,所以需要找一种方式对其进行解偶。在诸如java等面向对象语言中,依赖注入 Dependency Injection 就是一种很好的解偶方式。 所谓依赖注入,简单的理解就是如果组件Car依赖组件Engine,并不直接在组件Car中new出组件Engine,取而代之的是在外部创建出组件Engine,然后传递(注入)给组件Car,如下图:
接下来,我们需要创建一个提供时间的接口IDateTimeProvider,如下:
然后通过SmartLightController的构造器,注入IDateTimeProvider接口实例对象。并且在getTimeOfDay方法中,通过接口IDateTimeProvider实例获取时间。如下:
通过依赖注入的方式,我们将不同IDateTimeProvider的实现类传给SmartLightController类,这样就将SmartLightController和Calendar完全解耦。 单元测试 通过依赖注入实现解耦之后,再加上IDateTimeProvider接口加持,单元测试也变得极为简单。我们可以通过Fake Test Double的方式实现不同模式下的IDateTimeProvider,如下:
创建好上述Fake Test Double之后,就可以书写针对性的单元测试代码了,如下:
注:为了测试方便,我在SmartLightController中添加了getLastLightMotion来获取当前保存的灯光模式。 总结 单元测试和代码质量是相辅相成的关系,好的代码很容易对其书写单元测试,通过单元测试也能提前预知代码中可能会出现的问题。 如果发现项目中的逻辑代码很难书写单侧,很有可能就是耦合性太高。这样的代码健壮性不高,后期扩展成本也很高。本文介绍了一种很常用的解耦方式:依赖注入。 实际在Android中,还有一种更为高级的依赖注入方式--Dagger。下篇文章将会在这篇文章的基础上,添加Dagger的使用,并介绍如何对Dagger依赖书写单侧代码。
如果你喜欢本文 长按二维码关注 |
|
开发测试 最新文章 |
pytest系列——allure之生成测试报告(Wind |
某大厂软件测试岗一面笔试题+二面问答题面试 |
iperf 学习笔记 |
关于Python中使用selenium八大定位方法 |
【软件测试】为什么提升不了?8年测试总结再 |
软件测试复习 |
PHP笔记-Smarty模板引擎的使用 |
C++Test使用入门 |
【Java】单元测试 |
Net core 3.x 获取客户端地址 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/18 2:55:58- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |