最近状态不是很好,负能量堆到积爆表,静下心来看书写点东西才是出路。给瞎折腾的这两年,做个总结吧。
一些Practice和总结
充分单元测试能帮助项目快速推进。看起来单元测试会增加开发时间,但事实上大多数时候它不但会缩短当前功能开发到release的时间,还能为后续的功能的开发做铺垫。
单元测试覆盖率必须保证绝大部分业务逻辑被覆盖。实践起来就是,除了一些异常分支,覆盖所有可以覆盖的代码。这样单元测试才能起到哨兵的作用,在写新逻辑和重构代码的时候,保证一旦旧代码的正确性被破坏,单元测试马上fail预警。如果单元测试的覆盖率不够,是无法被依赖的。单元测试的作用得在覆盖率达到一个比较高的值的时候才能完全展现。
单元测试有助于提高代码质量。由于需要考虑代码的可测试性,有利于促成依赖注入(DI)、控制反转的设计。控制代码之间的耦合程度。站在开发者的角度来说,test case有助于提高开发者的编码和设计能力。一点经验是慎用单件(Singleton)。单件其实就是OO世界的全局变量,所有使用单件的代码都会和它强耦合,在测试的时候难以隔离。项目中遇到很多复杂繁重的单件,需要大量时间初始化,严重拖累了test case的执行效率。
用循序渐进的方法处理遗留代码,小规模重构,低痛低风险;持续重构,零修碎补。我刚进入项目的时候,项目有大概两万行的遗留代码,里面有比较多晦涩的业务逻辑,代码风格和结构都有一些问题,测试覆盖率不到30%。没时间完全重写,只能循序渐进,每开发一个新功能,重构一个老模块并补充test case,大概用了半年重构了大部分代码、补充test case。并且每次新feature开发都会分出一部分时间对之前的代码进行重构。
童子军军规:走的时候要比来的时候干净。破窗效应:一扇窗户被打破不修理,很快会有越来越多的窗户被打破(Bob大叔《Clean Code》)。这两条的想达到的效果是促进良性循环、避免恶性循环。我具体的实践是:
- 保证每次代码提交test case覆盖率只能提高不能降低
- Fix原有代码中所有的编译warning、findbugs、checkstyle warning,并保证commit新代码不引入新的warning
通过坚持这两个原则,半年后项目代码质量有明显提高,test case覆盖率从30%提升到85%,sonar的Rules compliance从80%提升到95%,代码重复率从10%降低到0%。而代码质量的提升对开发效率的帮助也是明显的,bug定位、添加新feature变得容易,功能测试发现的bug数和消耗的时间都有减少。
重视工具:流程固化在工具中,自动化所有重复工作。公司层面的实践是:有一个专门的team负责内部工具,并且这个team里都是最优秀的人;对于小型创业公司就是买适合自己的工具。流程是痛苦的,好的工具可以消灭或者减轻这些痛苦。目前的项目在这方面做的很不好,流程的实施,依赖人的职业性对抗人性的弱点,带来的后果是时间的浪费、人的疲惫以及生产效率降低。
尽量创造两人协作的环境。一个人孤独的做事对人对事的要求都太高(人要长时间自我驱动,事要靠谱),而三个人协作很难达到三倍的效率(沟通成本指数上升),但是两人协作却可以达到两倍的效率。两人协作可以既保证概念完整性又保证效率,父母抚养小孩是典型的例子(《设计原本》中文版P56 第六章 阐述了这个观点)。在复杂的项目里,很难把人数控制在两人,但也可以创造局部的两人合作。Two is the magic number。
测试金字塔
测试方面一个重要的总结是测试金字塔,其想表达的是:bug发现的越早,处理的成本越低。由此引出一个原则:尽量早地发现bug,尽量在开发过程中发现bug,而不是过度依赖QA,单元测试和持续集成是重要手段。下面的表格说明了bug发现阶段所增加的成本。
发现bug的阶段 | 相比在前一阶段修复bug的额外成本 |
---|---|
Coding | 直接改代码 |
开发期间部署测试 | Debug,重新ci代码,重新部署 |
QA | 和QA沟通,完成bug提交、确认、fix、回测的流程 |
上线后 | 处理用户反馈,重新走上线流程 |
用到的工具
- 单元测试框架JUnit/TestNG
- Spring Testing
- mock框架mockito、PowerMock
- lombok
- JRebel
- 持续集成工具jenkins
- teambox.com
一两年前JUnit和TestNG相比简直是弱爆了,现在JUnit也逐步赶了上来,补充了一些不错的feature,比如timeout、exception、Parameterized Test等实用功能,详见JUnit wiki
Spring TestContext Framework,实现测试组件的依赖注入,避免繁琐的setUp过程。 官方文档
mockito比JMock和easyMock有更优雅的语法 项目主页
用PowerMock测试static方法和final类 项目主页
用反射测试private方法(spring的ReflectionTestUtils)
编译期代码生成工具(JDK1.6+),自动生成getter/setter、toString、equals、hashCode、构造函数,checked exception转runtime exception,自动close资源等各种magic,项目主页
基于java agent的热部署工具,实现代码的热替换,避免重启web容器。大幅提高开发效率(尤其是对启动过程比较漫长的应用来说),支持大多数java ee容器。结合Ant的sshexec task可以实现一键从开发机更新代码到测试机,并且立即生效。项目主页
Jenkins结合Ant、覆盖率测试工具cobertura、java代码静态检查工具checkstyle、findbugs、sonar,完成每次提交自动构建、运行test case、计算test case覆盖率、检查代码bug的功能,极大的方便了对代码质量的监控(感谢测试开发同事的工作)。
项目协作的工具,一个SaaS,速度还凑合,对于小团队来说不错,基本功能是task管理,另外还有会话、文件共享、笔记等功能,5个人以下免费用,就是速度略慢。
This is not the end, this is not the beginning.
感谢Barton和Calphy。