一.前言html
不知道你们有没听过“测试先行的开发”这一说法,做为一种开发实践,在过去进行开发时,通常是先开发用户界面或者是类,而后再在此基础上编写测试。程序员
但在TDD中,首先是进行测试用例的编写,而后再进行类或者用户界面的开发。因为要先开发测试用例,那么开发人员就必须清楚测试的目的,所测功能模块的业务逻辑以及须要测试的场景。编程
这样TDD确保了项目的代码与所需的业务是匹配的,而且在往后的开发工做中也能确保以前所作的功能的可测试性。设计模式
不少同窗问TDD是使用那种编程语言,或者是某种技术,这里须要明确的是,TDD并非某种技术,而是一种项目实践。架构
导语:框架
传统开发模式与TDD开发模式的区别在哪里?TDD开发的困难之处和优势是什么?TDD具体开发过程当中又须要用到哪些技术知识点?且看本文做者经过实例来为你阐述TDD的开发流程,让你对TDD有一个大体的了解。编程语言
二.传统开发模式与TDD开发模式函数
1. 传统开发模式流程:单元测试
项目代码开发 -> 编写测试用例 –> 运行测试用例 -> 修复代码BUG学习
2. TDD开发模式流程
编写测试用例 -> 运行测试用例 –> 编写项目代码 -> 运行测试用例 -> 重构代码
三.TDD入门难题
说到写测试用例,通常的同窗都以为没啥问题,就根据已有的代码,顺着葫芦摸瓜的就把该测试的方法都照着“套”一遍,测试有哪些结果也得顺着项目的代码来。
至于这测试代码能不能测出项目的问题,那是另外的问题,关键是测试代码要Pass,那个人工做才能算完。
可是要在还没项目代码以前写测试用例,那就是等于我要凭空想象那些个抽象又晦涩难懂的功能点,还得要在心中勾勒出它们的轮廓以及细节,这不等于让我本身画个饼来充饥么?问题是我连这饼应该是啥样子都还没个谱,这是何等的悲凉啊,要是“蓝胖子”在我身边就行了~
四.TDD的优势
1. 保证代码质量,鼓励开发人员仅编写知足需求的代码。
“李雷”同窗号称马虎王,常常各类物品丢失,好比女朋友(对象)丢失;借用物品(引用)丢失;使用“IO自来水”以后不关阀门;去仓库(Database)取货,由于忘记某些物品,不得不频繁往返等等。
“韩梅梅”同窗功力深厚,精心打造出了一段瑞士军刀般的代码,耗时5天(其实此功能客户无扩展需求仅要求1天时间完成)。
而在TDD实践中,咱们须要注重代码质量,并编写恰好适量的代码。
2. 保证代码与业务需求的一致性
通常来说程序员都愿意把功能完美的体如今代码上,可有时候天不随人意,内心免不得担心,我这代码能知足业务需求么?但在TDD中,首先是进行测试用例的编写,而后再进行类或者用户界面的开发。因为要先开发测试用例,那么开发人员就必须清楚测试的目的,这样TDD确保了项目的代码与所需的业务是匹配的。
3. 建立简明有针对性的接口
一日,“李雷”接到“韩梅梅”发来的为某个功能准备的闯关宝典和核心步骤(类库与接口)。可读了3000遍仍是没有能理解,一方面是“韩梅梅”采用了古代文言文与现代拉丁语的混搭来书写核心步骤,另外一方面“韩梅梅”的“韩”式1到1000000的命名规则让“李雷”在读了30秒核心步骤后,已经不知道第几条是第几条,。
在TDD实践中,咱们要注重建立有意义的、简明的接口,由于这一点在与他人合做中尤为重要。
4. 与用户沟通,明确需求
在开发代码的过程当中,咱们总会有遇到不太明确的需求点,这个时候和需求人员沟通那是必不可少的,了解了功能的输入和输出才能保证完美的完成任务。在沟通的过程当中也加深了与客户的信任和默契度,不知不觉中还能提升EQ,一箭双雕。
5. 回归测试,确保新的更改不影响现有功能
在“韩梅梅”同窗开发某个功能3个月后,“李雷”接到上级指示,客户要扩展该功能,可是原有功能保持不变。在苦心操劳了以后,“李雷”同窗光荣的完成了任务,正准备接受你们赞誉时,“韩梅梅”跳出来向你们诉苦,那就是“李雷”为了作扩展功能把她以前作的功能给弄坏了,当时“李雷”那个心啊,拔凉拔凉的!
TDD的开发中加入了回归测试,这样就确保了以前的功能的正确与完整性,减小没必要要的问题。
6. 提高系统的开放性和扩展性
一直以来咱们作事都要讲前后顺序,软件开发也有着相似的工序。“李雷”和“韩梅梅”被一块儿“充军”到某紧急功能模块上,而且“李雷”要等“韩梅梅”完成她的功能模块才能开始本身的模块。为了解决这个问题,项目组决定使用某些技术来解除他们的依赖关系,好比使用到IOC以及一些设计模式,让他们可以同时开发,以后再将两人的功能模块组装到一块儿。
五.TDD开发中须要使用到的技术知识点:单元测试、依赖注入框架和模拟对象
1. TDD的工做流
TDD的工做流常常被描述为“红灯 -> 绿灯 -> 重构”:首先以一个未能经过的测试开始,随后编写足以经过该测试的代码,而后再重构代码。固然咱们都不肯意看到不能经过的测试CASE,当你再继续编写项目代码,让本来不能经过的测试CASE经过的时候,你会感受内心有一丝丝的惬意,而后再将代码优化重构,瞬间又有了些成就感。抿一口水,工做就这么快乐的完成了。
2. 伪对象、依赖注入框(DI/IOC)与模拟框架
就最简单的实践来讲,比较常见的三层架构,UI层去调用业务逻辑层,业务逻辑层去调用数据持久层。
“韩梅梅”作业务层的代码,“李雷”作数据层的代码,因而乎“韩梅梅”变成了“黄世仁”,“李雷”就成了“杨白劳”,其中辛酸只有“李雷”知道!为了改变命运,“李雷”决定作个“假”的数据层对象(模拟对象)给“韩梅梅”用着,省的她天天都在那催命。
伪对象是对代替外部资源的简单模拟,它一般会在调用一个方法时为该方法返回预约义响应,但一般不会根据输入参数而改变响应。
因而乎“李雷”欢乐的开始了他的计划,把“韩梅梅”所须要的功能点都用接口来实现(interface),而后把这接口的方法在单独的一个模拟类里面都只写了个简单的壳,里面的各类返回值都写成“韩梅梅”想要的数据样例,最后语重心长的对“韩梅梅”说:“东西拿走喜儿给我留下…”,“韩梅梅”固然是欢快的蹦到了本身的座位上。
控制反转是对象在被建立的时候,由一个调控系统内全部对象的外界实体将其所依赖的对象的引用传递给它。也能够说,依赖被注入到对象中。因此,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。
“韩梅梅”拿到李雷给的伪对象后,径直就用了起来,在全部须要伪对象的类里面都直接用了万能的“New”关键字来实例化这个伪对象,这招兼顾了简单与实惠,广大程序员爱好者都爱这么干。
可是 “韩梅梅”后来慢慢意识到不太对,我有许多地方都用用到NEW字,那不是之后“李雷”完成了他所谓的真的对象之后,我还必须得改个人代码,把我以前放进去的伪对象给替换为真正的对象?我这不是本身给本身找茬么。
“韩梅梅”赶忙找到带着黑框身背双肩包的师兄,细说了当前的苦衷。黑框师兄那舍得是师妹这么忧愁,赶忙拿出杀手锏“控制反转”中的一招“依赖注入”,让使用类中仅保留被调用对象的接口,而后动态的注入实例给这接口,这样子只要实现了这个接口的类均可以被任意替换使用,而且这个注入的动做通常是由某个框架来实现的,好比Autofac,、Unity或者Ninject等等。
这下子“韩梅梅”内心踏实了,管你“李雷,张雷,王雷”写什么伪对象或者真的对象,只要你的对象实现了指定的接口,我都能使用,并且我还不用本身去手动建立这个对象,省心又省时。
模拟框架是一系列用于快速建立伪对象的API,它能减小重复的代码,提升编码效率,比较经常使用的为Rhino, NSubstitute, Moq等。
“李雷”和“韩梅梅”就这么和谐的合做,可是随着任务的增多,发现问题来咯:
1) “李雷”要手工作不少的伪对象给“韩梅梅”,任务繁重。
2) 每一个对象的内部需求是不同的,“李雷”发现要用一种通用的格式来建立这些伪对象几乎是不可能的。
3) 不少伪对象又依赖于其余的伪对象,这样子简直就是要让崩溃。
4) 不少为对象内部有状态须要保存,手动来写代码很难去维护这些状态标示。
“李雷”好不容易经过建立伪对象来摆脱“韩梅梅”的每日请安,这又掉进了建立无数伪对象的漩涡之中。因而“李雷”横渡远洋,登山涉水,指望能找到一盏明灯解决这些问题,终于功夫不负有心人,“李雷”找到了神兵利器去解决这个问题,那就是模拟框架。
模拟框架帮助“李雷”快速的建立了各类“韩梅梅”须要的模拟对象,以及各类所需的API,弹指一挥间,李雷用这神兵利器已经杀敌无数,嘴角不禁得上扬了一番!
3. 重构代码
可是问题总归仍是有得,她发现本身有些功能虽然测试经过,可是代码写的很差,常常被黑眼圈师兄批评,说她的代码质量不高。
因此她必须尽量在刚测试经过以后就尽量的优化代码,一来是少挨骂;二来也是提升本身编码水平的一个机会,查漏补缺;三是贵人多忘事,况且是“我这等如花似玉的姑娘!”,若是不及早优化,恐怕之后很难有时间再来弄了。
由于已经有了测试代码,因此重构代码那也是颇有保障的事情,若是我改错东西了,那么我写的测试用例确定不能经过,这样子也能让我信心满满的去把这些个有臭味的代码大卸八块了。
六.工序流程
下面咱们来看看“韩梅梅”和“李雷”他们的工做步骤:
1. 首先,韩梅梅和李雷分析了他们各自的业务,而后韩梅梅写出了她须要测试用例,里面尝试使用“李雷”将要提供的方法,并经过此方法获取数据。固然这些代码第一次是测试不经过的,由于里面须要的实现类尚未写。这里咱们使用到Moq这样一个模拟框架。
测试用例的运行结果,你们也是知道的,两个字“悲催”!
2. 而后, “李雷”那边开始了数据持久层接口的编写(IProductRepository),“韩梅梅”拿到李雷提供的接口后,完成了业务逻辑层(ProductService)的代码编写,完毕以后大吐一口气:“小伙子终于给力了一次!”。
A. “李雷”的代码以下,实际上“李雷”只是提供了接口(interface)给“韩梅梅”,他还并无开始编写具体的实现类,可是韩梅梅已经能够经过该接口来工做了。
B. “韩梅梅”的服务类代码以下,她获取到“李雷”提供的数据持久层的接口后就开始欢快的编写代码,一切是那么的行云流水啊:
3. 接下来“韩梅梅”添加了各类须要的引用,再次运行起了测试用例,此次顺利的PASS了,内心那个激动,没的说!
4. 工做快要接近尾声,不过眼镜师兄提醒过“广大程序猿应该有高度的思想觉悟,竭尽全力的提升代码质量”,为了达成这一目标,“韩梅梅”又开始了上跳下窜的“你们来找茬”。
她发现里面有段代码写的很差,循环太多,也不够整洁,她想优化下代码,又怕把写好逻辑弄坏了,不过如今有了测试用例,她不会再怕有这个问题,改错代码,测试用例天然也就没法经过。
再运行下测试用例,依然经过,这次代码优化完毕,若是还有新的问题能够在依葫芦画瓢的继续优化。
5. 与此同时,“李雷”那边的数据持久层代码也差很少写好了,你们总得须要把代码合起来做“集成测试”,这个时候就要用到IOC框架来把“李雷”编写的数据层实例注入到业务逻辑层,注入实例使用的是Autofac这个IOC框架,咱们这里使用构造函数注入,关于注入框架的更多信息,请读者G….gle。
至此,“韩梅梅”与“李雷”各自的工做都完成了,你们也不在互相说啥,各自都优化了各自的功能代码,快乐的工做继续进行着,咱们的TDD讲解也到此结束!
参考文献:
本人最近开始学习TDD,藉此提高本身的能力。在学习过程会有一些心得体会,因而便会写一些博客来记录这些想法,有兴趣的朋友能够和我一块儿交流学习。
QQ群: 32745894,欢迎你们加入讨论!
http://www.cnblogs.com/zhq3051/p/4596049.html