通过之前文件关于单元测试的介绍,我们学会了如何减少代码的缺陷,通过回归测试抓取bug,减少对公共测试的依赖提高开发效率。本章我们的关注点将会放在测试驱动开发(TDD)上,他是测试先行、开发置后的开发模式。他有很多的好处,例如:更好的可测试代码、更简洁的接口和可以提高开发者信心的代码质量。
测试驱动开发的目标和目的
测试驱动开发的目标是简洁的代码。TDD是一种迭代的开发过程,每个迭代式以写单元测试开始,单元测试可以作为将要开发功能的规范文档。短期迭代对代码的反馈是及时的,这样可以更容易发现我们不好的设计。编写开发代码之前,先写单元测试还可以提高单元测试覆盖率。
1.开发置后
传统的开发模式中,问题知道代码全部编写完才得以解决。理想情况下,代码会有整体的架构考虑,但是很多情况下,特别是JavaScript开发的过程中,情况往往不是这样。这种解决方案,通过去猜什么样的代码能解决问题,导致的结果是代码肿胀和紧耦合。如果没有单元测试,产品代码中可能会包含一些没有运行的代码,例如异常处理逻辑。还有就是边界值可能没有被测试到。
TDD把开发顺序颠倒了,首先做的不是编写功能实现代码,而是考虑目标的制定,要实现什么功能和如何实现。单元测试在这里起到规范和文档的作用。TDD的目标不是测试,所以他不能保证在边界值的处理上做的更好。TDD虽然产生了额外的单元测试代码,但是他提高了系统的健壮性,而且保证系统不包含不执行的代码。
2.在TDD中做设计
TDD的特点是“没有大的预先设计”,并不是“没有预先设计”。为了写出整洁的代码,我们需要衡量整个项目的持续时间和考虑开发的生命周期,所以我们需要制定计划。TDD不会无中生有的自动生成好的设计,但是他会帮助我们进化我们的设计。通过对单元测试的依赖,TDD更多的把关注点放在了相互独立的、隔离的组件上。这种方式可以帮我们我们写出接偶的代码,代码遵循单一职责和避免不必要的膨胀。TDD提供了严格控制,可以将很多设计的决定时间延迟到直到真正需要的时候。他可以很好的应对需求的变化,因为我们很少设计不需要的功能,或者不需要按照预先期望开发。
TDD驱使我们去处理设计,当有了新功能的时候,我们需要以单元测试的形式制定合理的用例。写单元测试时需要思考的,我们需要描述我们在解决什么样的问题。只有完成这个工作,才能开始编码。换句话说,TDD要求我们在提供解决方案之前,要先想想结果。
促进测试驱动开发
测试驱动开发最关键的是运行测试,测试需要能快速而容易的运行。如果不是这样的话,开发人员就会忽略测试,当开发了新功能之后也不会运行测试。这样会让开发变得一团糟。最糟糕的是,我们花费额外的时候使用TDD的开发模式,却没有起到我们期望的作用,我们开发的代码还是一团糟。所以,顺利的运行测试是相当重要的。
推荐的方案是使用自动测试(autotest),每个测试保存在独立的文件中,可以单独运行。他能在屏幕上显示测试结果,告诉我们哪些测试通过了(显示绿色),哪些测试正在运行,和哪些测试失败了(显示红色)。这样可以提高我们开发的效率,帮助我们重构代码,我们只需要关注测试失败的情况。
测试驱动开发的好处
1.能够工作的代码
TDD最大的好处就是生产可以工作的代码。一个基本的单元测试可以确保一段代码的稳定。可再生的单元测试在JavaScript中特别有用,我们需要在很多浏览器平台上进行测试。测试代码只需要写一次,通过测试可以很快找到不能工作的代码,发现bug。
2.遵循单一职责原则
在隔离的条件下描述和开发组件,能更容易的写出解耦和符合单一职责原则的代码。TDD的单元测试不需要测试组件的依赖,他们需要能用mock或stub的方式替换。另外,单元测试也可以帮助我们找到程序中紧耦合的代码。
3.迫使有意识的开发
因为在每次迭代之前,我们都是先写描述特定功能的测试,TDD就迫使我们在编写代码之前先进行思考。在解决问题之前先进行思考,有助于我们找到一个更可靠的解决方案。通过用例对功能进行描述,也有助于我们开发出更简明的代码。这样也会避免我们引入不必要的功能。
4.提高生产效率
如果你是第一次接触TDD,你会觉得所有的测试和步骤都需要你花费更多的时间。使用TDD从一开始也不是那么简单的事情,写出好的单元测试需要不断的练习,本系列课程会通过很多例子教会你如何完成这个工作。当你养成了好的TDD开发习惯的时候,他确实能提高你的开发效率。他可能需要你多花点时间去完成功能代码和测试代码的编写,但是他能减少你手工测试的时间,取而代之的是运行单元测试。最重要的是,你开发出了有单元测试保障的、能工作的代码,代码重构的工作将不会变得那么荆棘。你的工作会变得更快速、压力更少和更快乐。