意图代码+错误信息驱动+Mock单元测试:bjdp.org第18次编程道场回顾
编程道场中的Q&A: TDD在实战中会如此简单吗?Mock在实际工作中的应用场景有哪些?代码重构如何做?如何知道代码内在质量十分良好?高质量代码的度量?开发人员自测少该怎么办?……
感恩京东商城北京总部的蔡德辉先生和宋宁女士帮忙为本次活动提供场地!
* 时间:2014.08.22, 2:00-6:00pm
* 地点:京东商城北京总部
* 参加人数:28人
* 活动主题:意图代码+错误信息驱动+Mock单元测试(Java版)
* 伍斌完成该题目的源代码链接:
1)原题:https://github.com/wubin28/tbc-ticket-dispenser-java/tree/exercise
2)手写mock解决方案:https://github.com/wubin28/tbc-ticket-dispenser-java/tree/subclass-and-override-method
3)使用mockito框架的解决方案:https://github.com/wubin28/tbc-ticket-dispenser-java/tree/using-mockito-error-fixing-oriented
* Ticket Dispenser题目描述:编程题目的源代码来自ThoughtWorks Studio的培训师和教练Luca Minudel在2012年设计的C#编程操练系列题目TDD with Mock Objects。编程题目是有关自动取号系统的,类似于去银行办事进门时取号的那种取号机。有3个类:TurnTicket类表示要取的票,保存着票上印着的号码;TurnNumberSequence类用于产生所有票上的号码;TicketDispenser根据从TurnNumberSequence类获得的号码来出票。操练要求在现有代码的基础上实现两个新特性:1)VIP客户的票号从1001开始;2)普通客户的票号从2001开始。在实现新特性之前,首先要为TicketDispenser类编写单元测试,而且要求在有多个取号机TicketDispenser的情况下,两个人分别在两台取号机上不能取到同样号码的票。
活动过程
1. 各位匠友自我介绍与编程中最大的问题收集
2. 伍斌现场编程演示用意图测试+意图代码+错误信息驱动+手写Mock+子类化并覆写方法+Mockito框架的方法,来为TicketDispenser类添加单元测试,并实现新特性
3. 各位匠友定时自由结对编程操练上述编程题目(每对7分钟)
4. 回顾与问题解答
回顾要点
Well
* 组织者的确是一路IT人士,气质相近,不玩虚的。
* 了解Mock开发的流程。Mock讲得好。
* 针对开发,用测试驱动开发的编程模式讲得好。
* 接触到新的开发模式和思维,对TDD和mock有了认识,需要看的书又多了几本。
* 问题取之于众,授之于众。
* 代码重构,通过实例讲解,更好理解。课堂演练较好。
* 代码实战比较多,例子比较简单,容易理解。较通俗。例子讲解比较清晰。
* 深入讲解理论知识,知识覆盖全面。
* TDD操练、Mock、动手写代码。
* 新的开发逻辑。测试驱动开发这种思想挺好的。
* 教学方式不错,例子有吸引力。
* 开发思路。开发中代码单元测试。代码调试成本降低。Mock思路。
* 学习了一个新的工具。代码层级的测试环境搭建。
* 测试先行。从assertEquals()开始做意图式开发。
* 组织者向我们展示了整个测试驱动开发的过程,接触了Mock等一些新想法,觉得非常好。
* 本次活动受益很多。讲解内容很实用。敏捷测试代码。思想有所收获。
Less Well
* 动手练习环节可以提高。
* 技术落地环节有待加强。
* VIP取号要求可以再花1~2分钟讲解一下,现场只看了test类。
* PPT下次可以先发一下。
* 缺少些成功案例的大概实施方式。(Ben: 可以参考微信文章“重构实践:百度工程效率部技术教练经验分享” http://mp.weixin.qq.com/s?__biz=MzA5Mzg0OTgwMA==&mid=200778187&idx=1&sn=4fd205acb9d704b94b6e5eeaa7027d42&scene=1#rd)
* Mock如果能深入可能更好。
* 主题很大,例子很小,无法深入。
* 推荐与总结不够。
* 希望多些案例讲解,使人更容易理解。
* 课堂不太紧凑,缺乏有效的互动。
* 组织者对我的问题的回答并未令我满意。
Puzzles(含Ben个人的解答)
1)TDD在实战中会如此简单吗?Mock在实际工作中的应用场景有哪些?
答:TDD的概念其实很简单,这个概念在实战中也是如此简单。但是要在实战中用好TDD,就不是那么简单了。因为这需要将自己以往的测试后行和针对实现编写测试的习惯,转变为更利于代码内在质量的测试先行和针对用户意图编程的新习惯。习惯的转变是最难的,需要大量业余时间进行刻意的操练。
在实际工作中,只要被测系统SUT(System Under Test)所依赖的组件DOC(Depended-On Component)难以在测试中被控制,就可以使用Mock。
2)重构的全面介绍?
答:请参阅Martin Fowler的名著《重构》。
3)高质量代码如何写?
答:首先需要定义什么是高质量的代码。个人认为高质量的代码就是反馈快的代码。这个问题比较大,简而言之,个人认为使用测试先行的TDD开发方法是写高质量代码的好办法。不妨参考本人即将出版的拙作《驯服烂代码:在编程操练中悟道》。
4)代码重构如何做?
答:首先需要编写能表现用户意图的测试代码,将现有代码的行为固化下来,然后在测试的保护下,对生产代码和测试代码做全面的重构。没有测试保护的重构,不能算重构,而是裸奔。
5)如何知道代码内在质量十分良好?高质量代码的度量?
答:不妨借助一些开源工具来做分析和统计,比如SonarQube: http://www.sonarqube.org。
6)如何提高复用性?
答:要提高复用性,首先要去除重复代码。在去除重复的重构过程中,会提取一些公共的方法,从而提高复用性。
7)何时重构?程度如何?
答:一般在已有的代码上添加新功能或修改Bug,遇到代码腐臭时,首先编写测试,然后再重构。重构到程序员再也闻不到代码腐臭时为止。
8)TDD测试用例覆盖到何种程度?
答:覆盖到满足所有用户意图时为止。
9)注释该如何写?
答:用富有表达力的类和方法的命名来替代注释。要让注释主要用于记录尚未消除的代码腐臭和尚未完成的用户意图测试等TODO,或描述一些已经有很多客户端使用不便更改且命名糟糕的公共接口。
10)开发人员自测少该怎么办?
答:最根本的解决之道时要让开发人员养成测试先行的习惯。这需要从公司高层的合理支持和基层有TDD习惯的程序员的影响这两方面共同努力。
11)单元测试如何做?步骤是怎样的?
答:首先寻找用户意图,然后编写用户意图测试,当被测系统SUT(System Under Test)所依赖的组件DOC(Depended-On Component)难以在测试中被控制,可以使用Mock。然后通过逐个修复意图代码的编译错误来驱动生成生产代码并让意图代码编译通过。接着运行测试,并通过让测试运行通过来驱动生成生产代码。最后在符合“道”的前提下,全面重构生产代码和测试代码。详情参见本人即将出版的拙作《驯服烂代码:在编程操练中悟道》中有关TDD的一种实现I-EPP-TR方法的描述。
12)维护别人代码的策略?
答:同上述单元测试的步骤。
13)如何提高代码的可读性和可维护性?
答:提高可读性需要用富有表达力的代码中的命名来描述用户意图,最好需要提高英文写作能力,不妨去公益教育组织www.toastmasters.org来操练英文演讲和写作。提高可维护性的方法参见上述单元测试的步骤。
14)如何做到测试先行?
答:这需要在认同测试先行所能带来的好处的前提(参见我写的微信文章“TDD能让软件开发变快的三个理由”:http://mp.weixin.qq.com/s?__biz=MjM5MjEwNTEzOQ==&mid=200543963&idx=1&sn=2af72bb687b89a266d4fe54a387ab2ec#rd)下,通过长期刻意的编程操练,来掌握相关技能,进而养成习惯。
15)如何维护测试代码?
答:这个问题比较大。个人认为要想编写易于维护的测试代码,需要遵循依赖倒置原则(DIP),来令测试代码依赖于用户意图这个抽象,而不是生产代码的实现。
16)开发的原则有哪些?
答:一般有5个原则,即面向对象的SOLID设计原则,SOLID是下面5种设计原则的英文首字母:单一职责原则SRP(Single-Responsibility Principle)、开闭原则OCP(Open/Closed Principle)、里氏替换原则LSP(Liskov Substitution Principle)、接口隔离原则ISP(Interface Segregation Principle)和依赖倒置原则DIP(Dependency-Inversion Principle),参见Robert C. Martin所著《敏捷软件开发(原则、模式与实践)》一书。
17)在结对编程操练中,编写了两个test case。请问在实际开发中,是测试工程师编写,还是开发人员编写?
答:如果是TDD开发,一般由开发人员编写。如果是涉及产品经理、测试工程师和开发人员的ATDD/BDD开发,则可先由测试工程师在这三方人员的实例化需求工作坊中编写自然语言描述的测试用例,然后编写粘合代码,最后和开发人员结对编程来令这些实例化需求测试真正运行通过。
18)既然开发人员编写单元测试,那么测试人员还需做单元测试吗?
答:一般测试人员不必做单元测试,而可以做实例化需求测试。
19)针对测试工程师而言,编写单元测试貌似没有太多用处?
答:看似没有用处,但是将来的趋势是开发人员与测试人员之间的技能相互融合。所以感兴趣的测试人员用业余时间操练单元测试也是对将来职业发展很有益处的。
20)QA与开发人员如何合作?
答:最好双方与产品人员一道,三方用ATDD/BDD的实例化需求来合作,越早合作越好。
21)QA少时如何推动单元测试?
答:QA无法推动程序员写单元测试。推动程序员写单元测试的方法参见上面“开发人员自测少该怎么办?”的答复。
22)手工的功能测试如何与自动化测试相结合?
答:这个问题可以参考Mike Cohn的自动化测试金字塔(Test Automation Pyramid, http://www.mountaingoatsoftware.com/blog/the-forgotten-layer-of-the-test-automation-pyramid)的概念。简单说来,该金字塔分3层,塔尖是UI,中间一层是Service,最底层是Unit。大量的自动化测试应该集中在最底层的自动化单元测试,中面Service层的自动化测试会数量少一些,而最上层的用户界面的自动化测试应该最少,因为在这里做自动化测试投入产出比最低。由此可见,手工测试应该集中在最上层的用户界面的测试。但是手工测试中一切能自动化的部分(比如测试环境搭建)都要尽量写脚本来实现半自动化。
23)测试工程师在开发中何时介入测试?
答:越早介入越好,最好以实例化需求ATDD/BDD的方式介入。
24)如何面对需求变更?
答:不是所有的需求变更都具备同等的优先级。不妨参考MVP(Minimum Viable Product, 最小可行产品)的理念,优先把解决符合MVP的需求变更。需求的变更势必会导致修改测试代码,来反应新的用户意图。
25)代码质量与进度之间的如何平衡?
答:良好的代码质量能加快进度。但是前提是每位程序员都有编写具有良好质量的代码态度、技能和习惯。这需要大量的业余时间的操练。
26)如何衡量测试和单元测试自动化的投入产出比? ROI如何度量?
答:没有研究过软件度量。建议参考下面的书籍:1)《程序员度量》,2)《精益软件度量》,3)
SonarQube in Action by G. Ann Campbell and Patroklos P. Papapetrou
27)请为一个刚毕业的新同学,给出一份程序员进阶必读书清单。
答:可以参考ThoughtWorks的读书雷达(http://wenku.baidu.com/link?url=gzF1IJLQZnoIJ4okzW6SkwZ6MJ2XjT1RK2T1UmTGTgGhRHLH5_Ph5SK8OEjZ3ke0pHHrD8vIf-9sCYfq4sqd7rcpC-CH1sqig94H6iFdoNa),先读其中感兴趣的,量力而为,循序渐进。同时与编程操练结合起来,必有收获。
28)啥时候再讲?
答:bjdp.org第19次编程道场计划于2014年10月19日下午在ThoughtWorks北京办公室举办,编程语言计划为Java7 + Java8 + Scala,编程环境为OS X + IntelliJ IDEA。编程题目与函数式编程范型相关。报名信息9月下旬会在此微信订阅号中发布。
--------------------
操练成就匠艺。全栈开发者的公益编程操练社区:bjdp.org北京设计模式学习组。微信订阅号:bjdp_org