背景
两年前,我在一个Web项目开发组中,项目的目标是编写一个类似Excel的、用来计算产品/服务价格的Web应用程序。项目团队被分成3部分——开发团队、需求团队和QA团队。随着项目越做越大,而我们没有使用任何形式的自动化测试(QA团队使用手工测试),结果导致项目的测试时间比开发时间还要多。每进行一次小的改动,QA团队都要花费几个小时来做测试。
有一天,我参加了一个开发者会议,并与其他程序员谈到了这些问题。他们建议我去学习单元测试、验收测试和TDD(Test-Driven Development,测试驱动开发)。
我的经验总结
下面是我在学习集成测试/TDD过程中的一些经验教训,希望能够对大家有所帮助。
1. 不要第一次就在真实项目中尝试TDD
这可能会让你的项目很难进展。在采用TDD之前,你必须要了解TDD的工作流程以及如何去模拟对象(mock objects)、如何去模拟框架内部、如何组织测试等方面知识。因此,如果你的团队还没有准备好,就采用TDD可能会拖慢你的项目,从而错过最终交付期限。
2. 采用编程道场(Coding Dojo)方式学习TDD
我们发现编程道场是对新进入团队的开发者培训TDD以及提升他们编程技能的最好的方式。
编程道场是一种帮助提高编程技能的训练方法。一般是在一个会议室里,有一台连接投影仪的电脑。每次道场还要有一个挑战题目。每次有两个人在电脑前结对编程来试图解决挑战的题目,并且由其他参加者提供建议。所有的参加者都要轮换到结对编程里进行演练。(来自维基百科)
3. 应用TDD之前尝试说服你的整个团队
没有什么比团队中存在“破坏者”角色更令人沮丧的了。如果团队中大部分人都在朝着TDD努力,而个别开发者所做的工作有可能毁掉这些努力,比如提交失败的测试代码等。我曾经就与这类人一起工作过。在团队开始TDD开发之前,一定要让所有人明白它的好处,并了解如何测试可以使软件减少bug、如何重构代码而不用担心破坏整个项目等等。
4. 写足够多的测试
构建一个测试套件,就等于构建了一个bug防护盾。当团队重构或改进软件时应该完全信任这个“盾”。如果这个盾有缺口,那么我们在更改代码时会增加引入未知bug的风险。不要强求测试套件覆盖100%的代码,这是不可能的,而且相当耗时,但是,覆盖大部分代码是完全可以实现的。一个好的准则就是测试所有可能会出现问题的地方。
5. 使用覆盖率工具
覆盖率工具将会报告测试套件的缺口。借助这些工具,可以很容易识别哪些代码没有测试。这些工具可以给我们一个直观的认识,比如蓝/绿着色线表示正在测试中的代码,红色着色线表示没有测试。如果你是一个.NET程序员,Visual Studio旗舰版会带有这个功能;如果你是一个Java程序员,你可以使用EclEmma。
6. 测试速度要快
当在构建软件时,我们永远都在追赶最后的交付期限。我们的测试应该帮助我们实现这一目标,而不是耗时和延迟。
如果写测试用例花费太长时间,在最后期限到来之前,团队可能不会再花时间写测试。如果在运行测试上花费太长时间,团队可能不会在每次更改代码后都运行它们。
7. 不要忽略失败的测试
如果你的团队对第1次失败的测试不在意,那么他们对第2、3、4次失败的测试也不会太在意。这种情况下,测试套件反馈的问题将会被忽略,测试也不会对软件质量有太大帮助。
8. 结对编程有助于团队应用TDD
当我们首次试图采用TDD时,或者最终交付期限临近时,我们可能会忘了写测试,而只顾写生产代码。结对编程可以阻止这种偷工减料的现象,强制团队成员编写测试。
9. 保持你的测试代码整洁
为了提高工作效率,我们的测试代码可能不会像生产代码一样整洁。由于软件发生改变时,测试代码也必须改变,测试代码也会越积越多,这样一来,会导致最终测试代码也很难维护。
10 测试应该有且只有一个失败的理由
这个理由就是发现bug。如果你的测试代码有大量的断言,你就需要小心。如果生产代码中的函数和类只有一个响应,那我们的测试代码也应该只测试这一个事件。通过这种方式,将很容易找出失败的测试,并弄清楚什么地方出问题了。
11. 编写单元测试将节省调试时间
通常在调试代码、寻找bug时会花费大量的时间,一旦你编写了单元测试,你将会在代码的每一块得到一个实时反馈,这将让你更容易找到bug,节省调试时间。
12. 持之以恒
应用TDD可以改变我们的心态。对于部分开发者而言,开始写测试很难,而在写生产代码之前写测试就更难了。重要的是,要持之以恒地写测试,一旦你的团队完全适应了这种方法,生产效率会成倍提升。
11 楼 fortaotao 2013-07-16 08:49
10 楼 StrongZhu 2013-07-14 00:00
1、将开发人员用于debug的成本投入,转换成可以持续运行的测试用例,该部分投入将变成资产。
2、测试用例可以将复杂的业务实现代码,拆分成以小逻辑单元,简化验证的复杂度。体现到覆盖率指标上即应追求line coverage,但condition coverage的全覆盖是可商榷的,即单元测试无法替代集成测试。
3、让团队中的所有人对其他人产生信心
9 楼 yunzhu 2013-07-12 11:00
这篇文章我先发到CSDN,然后发到ITeye,博客园转载的是CSDN的,转载速度快了点而已
好吧,是我错了,主编辛苦了
原来现在都是在CSDN和ITEye同步发啊
8 楼 wangguo 2013-07-12 10:49
这篇文章我先发到CSDN,然后发到ITeye,博客园转载的是CSDN的,转载速度快了点而已
7 楼 yunzhu 2013-07-12 10:44
6 楼 China祥 2013-07-12 09:27
5 楼 id.alex 2013-07-11 16:05
. 写和维护 TDD 代码的代价.(没做过 TDD 的请体验下再来发言...)
. 测试数据的准备.
. 只测试正确的逻辑,最好不测试错误场景.或者有选择的测试错误场景.否则带来的工作量是 N 倍.
. 测试代码覆盖率的 KPI, 这个非常坑. 有很多 try catch , 异常流程是没有测试的,否则情况如上.
. 依赖第三方接口的测试,这个 Unit Test 不能真的去调用,但 Mock 很难 Mock.注释掉代码又跑不通,或者没意义.
. 端口绑定/调用脚本的 Unit Test.
. 多线程并且有数据库修改的 Unit Test 难做. 原本代码是没有事务的,但为了 Unit Test 的事务回滚,不可能加上.
一时想到的就这么多....其实在做的时候还会遇到更多的问题,不能或者说不容易去做 TDD.
而且,最重要的,
真的确定原来的 TDD 代码是正确的么?
测试跑的通和代码没有错误,是完全完全不同的概念.!!!
4 楼 yq81862 2013-07-11 13:46
3 楼 jinnianshilongnian 2013-07-11 11:50
其实我认为测试也很简单,就是
1、可能出现的问题,写个测试用例 保证代码是预期的执行
2、如果不是预期的:要么逻辑错了,要么用例错了
3、如果用例正确,当修改代码后 可能重复执行用例,在项目中,只有是这种情况才是比较有用的(不考虑框架级)
2 楼 jinnianshilongnian 2013-07-11 11:48
1 楼 lection.yu 2013-07-11 11:16