6.1 JavaScript提出的问题


 读到这里,应该很清楚了,要想真正很好地使用Ajax,你就要写一些JavaScript代码。尽管框架和工具包能减轻一些负担,但是最终你可能会得到比平常更多的JavaScript代码。因为我们自己写过不少,所以很清楚写JavaScript代码绝不是轻而易举的,不过,本章我们还是要在你筋疲力尽的肩头再压上几块石头。

  具体地,我们将介绍测试驱动开发(test-driven development,TDD),并展示开发JavaScript代码时如何应用TDD。尽管这种方法不能马上解决你的所有编程问题,但至少能帮助你尽快完成工作,能按时回家与家人共进晚餐。我们先对TDD和广泛使用的JUnit做一个简要介绍。打好基础之后,我们将讨论JsUnit,并说明如何编写和运行测试。

  6.1 JavaScript提出的问题

  如果你参与过Web应用的开发,可能已经写过一些JavaScript代码;当然,如果你只是写了一些最简单的函数,那么对JavaScript的看法可能不会太好。浏览器不兼容,缺少优秀的开发工具,没有代码完成(code completion)之类的生产力工具,没有调试工具——这么多的缺点,足以让大多数开发人员更乐于使用vi[1]。

  我们很清楚你的这些苦衷。在第5章中,已经讨论了许多工具,它们能让你的日子更轻松。本章将介绍如何让开发JavaScript尽可能地容易(至少,在工具开发商迎头赶上之前,这种方法很合适[2])。采用测试先行(test-first)的方式来编写JavaScript,能大大简化整个开发过程。

  6.1.1 测试先行方法介绍

  是的,现在肯定有读者会这样说了:“我只在产品发品之前写测试。”有些人可能会窃笑,对质量保证部门说三道四。还有一些人作为项目经理可能会添油加醋地说:“我们可不会浪费时间写测试代码;我们还得写真正的代码呢。”那么,采用TDD到底是什么意思呢?

 TDD产生于敏捷开发运动,特别是极限编程(extreme programming,XP),而且TDD正是XP的一个核心原则。推崇TDD的人认为,不应该完成开发之后再写测试,这通常只是“马后炮”,而应当在写代码之前先写测试。测试本质上相当于设计文档,而不是花大量时间去摆弄一个复杂的图形化工具,你要直接在代码中“拟画”一个类。开始时先为一些小功能块编写测试。在有些语言下,你写的测试甚至不能编译,因为所引用的类尚不存在。一旦建立了测试,就可以运行这个测试(当然,此时运行测试会失败)。然后再编写最少量的代码,以便测试通过。接下来再重构代码,并增加更多的测试。

  通常可以使用测试框架来帮助编写自动化测试。最着名的框架是JUnit,不过现在已经有很多xUnit项目,可以简化在各种语言下测试的创建。一般地,这些框架都建立在断言(assert)基础上。开发人员编写测试方法,将调用方法的实际结果与期望结果进行比较。当然,可以人工地检查一个日志文件或用户界面来完成这些比较,但是,用计算机完成数据比较不仅速度快,也更准确。另外,就算是让计算机把同样的测试运行上1500次,它们也不会嫌烦,如果是人可做不到这一点。

  有了JUnit和其他测试框架,编写和运行测试变得相当简单。这就能鼓励开发人员创建大量测试(往往能更完备地覆盖各种测试情况),而且会乐于经常运行这些测试(可以更快地帮助开发人员找到bug)。在许多情况下,如果项目中采用了TDD,测试代码往往与生产代码一样多!

  使用TDD可以带来许多重要的好处:

  提供明确的目标:你很清楚,一旦结束(也就是一旦测试通过),你的工作就完成了(假设你的测试写得很好)。测试会为代码建立一个自然的边界,使你把重点集中在当前任务上。一旦测试通过,就有确切的证据证明你的代码能工作。相对于人工地测试用户界面或者比较日志文件中的结果,在一个xUnit框架中运行自动化测试,速度要快几个数量级。大多数xUnit测试的运行只需几微秒,而且大多数采用TDD的人都会一天运行数次测试。在许多开发小组中,将代码签入源代码树之前,代码必须成功地通过测试。

提供文档。你是不是经常遇到看不懂的代码?这些代码可能没有任何文档说明,而且开发代码的人可能早就走了(或者度假去了)。当然,看到这种代码的时间往往也很不合时宜,可能是凌晨3点,也可能有位副总在你旁边大声催促着赶快解决昨天的问题,这样要想花些时间来明白原作者的意图就更困难了。我们见过一些好的单元测试文档,它们会指出系统要做什么。测试就像原开发人员留下的记号,可以展示他们的类具体是怎么工作的。

  改善设计:编写测试能改善设计。测试有助于你从界面的角度思考,测试框架也是代码的客户。测试能让你考虑得更简单。如果你确实遵循了“尽量简单而且行之有效”的原则,就不会写出篇幅达几页的复杂算法。要测试的代码通常依赖性更低,而且相互之间没有紧密的联系,因为这样测试起来更容易。当然,还有一个额外的作用,修改起来也会更容易!

  鼓励重构:利用一套健壮的测试集,你能根据需要进行重构。你是不是经常遇到一些不知是否该修改的代码?种种顾虑让你行动迟缓,过于保守,因为你不能保证所做的修改会不会破坏系统。如果有一套好的单元测试集,就能放心地进行重构,同时能保证你的代码依然简洁。

  提高速度:编写这么多测试会不会使开发速度减慢呢?人们经常会以速度(或开发开销)作为反对进行TDD和使用xUnit框架的理由。所有新的工具都会有学习曲线,但是一旦开发人员适应了他们选择的框架(通常只需很短的时间),开发速度实际上会加快。一个完备的单元测试集提供了一种方法对系统完成回归测试,这说明,增加一个新特性之后,你不必因为怀疑它会不会破坏原系统而寝食难安。

  提供反馈:单元测试还有一个经常被忽略的优点,即开发的节奏。尽管看上去好像无关紧要,但通过测试之后你会有一种完成任务的成就感!你不会成天地修改代码而没有任何反馈,这种测试—代码—测试的方法会鼓励你动作幅度小一些,通常修改一次代码的时间仅几分钟而已。这样你不会一下子看到冒出一大堆的新特性,而只是让代码基一次前进一小步。

 从我们的经验看,测试是会传染的,你可能会慢慢上瘾。一开始,许多开发人员都心存疑虑,但最终几乎每个开发人员都迷上了运行测试后的绿条[3]。测试第一次“抓住”bug或者增加一个新特性,只需几分钟而不是几个小时,往往就是在这样一些时候,开发人员会欣喜地认识到测试确实很有意义。

  6.1.2 JUnit介绍

  由于JsUnit的出现源自JUnit的启发,所以我们先对JUnit做一个简单介绍,然后再深入地分析JsUnit。关于JUnit有一些非常好的书,若要想详细地了解JUnit,可以参考一下。虽然JUnit不是测试的惟一选择(TestNG和Fit/FitNesse也很值得研究),但是它与JsUnit有着密切的联系,实际上后者相当于为了测试JavaScript,而开发的JUnit“移植版”,这是我们首先讨论JUnit的原因。

  JUnit是使用最广泛的xUnit测试框架之一。JUnit是Erich Gamma和Kent Beck编写的,通常用于测试基于Java的开源软件,而且最常用的IDE都对JUnit提供了充分的支持。用JUnit编写测试相当简单,只需创建一个实现TestCase的类,编写一些以test开头的方法,其中设置一些断言,然后用你最喜欢的工具来运行这些测试。默认情况下,JUnit会自动运行以test开头的方法,不过,你也可以根据需要改变这种行为。

  编写第二个或第三个测试时,你会发现有些公共的代码可以重构。你可能已经读过Andrew Hunt和David Thomas所着的The Pragmatic Programmer(Addison-Wesley公司1999年出版),应该知道要避免重复,所以会把一些公共的代码抽出到一个固定件(fixture)中,为此要覆盖setUp()和tearDown()方法,这些方法会分别在运行每个测试之前和之后调用。

  刚开始,你可能只有几个测试,但是慢慢地,测试会越来越多,而且需要某种方法来组织这些测试。在JUnit中,可以创建TestSuite,其中包括一个测试方法集合,甚至是整个测试类。(TestSuite可以包含实现了Test接口的任何类。)如果你想对测试有更多的控制,可以手工地把测试增加到TestSuite,或者可以告诉JUnit来为你完成这个工作,为此要把TestCase作为参数传递给TestSuite构造函数。


  JUnit支持许多测试运行工具。有些IDE有自己的专用运行工具,而且只要你愿意,还可以开发你自己的运行工具。JUnit提供了一个文本运行工具,还提供了一个图形化运行工具,它能报告运行测试所得到的结果。(图形化运行工具有一个方便的“红条失败”/“绿条通过”方法。)JUnit测试通常由提交或构建过程启动。

 

本文作者:
« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3