购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

前言

既然要对代码进行测试,那么为什么不让这一过程变得尽可能简单和轻松呢?JavaScript客户端代码测试之所以尤其困难,是因为我们几乎无法控制代码运行的环境。多种类型的操作系统、多个版本的操作系统、多种类型的浏览器、多个版本的浏览器,更不用说插件、扩展、多语言版本和缩放大小了,还有一些未知内容,所有这些因素交织在一起,阻碍着应用程序的性能。这些因素会导致程序变慢、中断、崩溃,最终覆灭。这里面的内容纷繁复杂!服务端JavaScript给了我们更多的控制权,以便我们能够从总体上控制执行环境。然而,Rhino和Node.js应用程序不像其他语言一样有完整的成熟工具、测试程序以及生态系统。此外,Node.js的异步特性也使得测试变得更加复杂。有趣的是,这样一种与异步执行密切相关的语言,竟然没有设置与该执行模式相配的内置支持。

无论如何,测试——尤其是JavaScript测试——是很复杂的。克服这种复杂性的最好办法是完全控制自己实际所控制的东西:代码。代码是连续存在的,一方面是从别人的代码到自己的代码,另一方面是从遗留代码到非遗留代码。

什么是遗留代码(legacy code)?我比较推崇Michael Feathers在他的优秀作品Working Effectively with Legacy Code(Prentice Hall出版社)中的定义:遗留代码是没有测试过的代码,这段代码将无法存活或永远不会被任何人接触到。再次接触遗留代码时,就是要重写它了。看一下当前的项目,任何没有被测试的代码都有可能会被重写。重写的人可能不是原作者,而是负责处理这个任务(增强代码或修补漏洞)的人。除非这些代码经过测试,否则它们就是必须要重写的无用代码。这段代码可能很惊人,但它唯一能存活的方法就是永远不会产生Bug,并且没有人要求对它进行增强或者添加新特性。即便如此,你愿意将这些未经测试的产品代码推到市场上吗?即使代码之前“能用”,之后你还能继续满意吗?拥有该代码的公司也是同样满意吗?因此,通常的结果都是付费重写。公司不得不再次付费进行代码重写,这真是太糟糕了,但这就是遗留代码的情况。

从图P-1所示的矩阵图中可以看到,遗留代码非常容易被别人重写。相比对别人的遗留代码进行测试,这种方式通常要好些。由于代码时常会易主,归你管或不归你管的情况都极容易发生,所以在矩阵中左右移动是很容易的。“向下”移动是编码中最难的一步,没有人愿意为已有的代码编写测试,相反,绝大多数人都会强力避免它——通常的结果是进行全部重写。

图P-1 当前代码与遗留代码

可惜的是,在这个矩阵中“向上”移动需要遵守一些规则。原先经过测试的代码如果被低手接管就会变成遗留代码。由于越来越多的增强和新功能需要引入,所以要时刻保持对代码测试的更新,但这一过程比为完全没有测试(或非常少的测试)的代码编写测试简单多了。

本书目标

本书的目标是通过采用一个整体的开发方法让编写的JavaScript代码处于图P-1的右下象限。这不仅是“测试编写”或“测试先行”,而是让大家了解到,在编码时所做出的选择,不管是好是坏,都将影响你后续的工作(代码和使用)。

先从养成在语法和语义上构建代码以实现可测试性的良好习惯开始,在正确的时间编写正确的测试,定期运行这些测试并查看测试结果,这样会让代码时刻保持在矩阵的右下象限。

本书试图在良好开发实践和JavaScript之间建立一个桥梁。JavaScript是一种古怪的小众语言,从其毫无意义的名称就可以看出,JavaScript最初用于非编程人员向Web页面添加一些互动。在语言建立之初,即使是“真正”的程序员使用,也得花一些时间才能适应其语言、DOM和浏览器环境之间的动态性。

随着越来越多的专业程序员开始使用JavaScript语言,这种语言的最佳实践开始被理解和编纂。JavaScript调试器、测试工具、IDE等更是开始不断涌现。该语言本身也是历经了多年的修改。JavaScript开始发展并茁壮成长,但是依然有很多怪异之处,而且也还没有出现更强大的工具。

随着Node.js、PhantomJS这类服务器端JavaScript的出现,现在一个应用程序可以全部用JavaScript来编写了。而在这之前,这不仅是不可能的,而且也被认为是疯狂的。但现在没有人再笑话这种做法了!

本书试图结合测试和质量保证(QA)两个方面几十年的研究和经验教训,将这些经验教训应用于JavaScript。本书中几乎所有的示例和代码片段都是用JavaScript编写(偶尔有一些Perl)。

目标读者

本书主要目标受众是那些想成为JavaScript专业开发人员的人,初、中级或者专家级的开发人员都适合阅读本书,因为每个人都可以从本书获取有用的知识。

JavaScript可能不是你所使用的唯一语言,但你在编写或测试程序时要用到大量JavaScript。如果有人付费(希望是)让你编写JavaScript代码在浏览器里运行,或者幸运的话,在服务器端运行,如果你每天都用JavaScript编写不同大小项目的话,本书正是你的不二选择。

如果你加入了一个必须要测试JavaScript的QA或工具团队,那么本书的第3章到第7章非常值得一读。本书的目的是使测试尽可能变容易,进而全部自动化。希望这本书能使大家的工作更轻松,这就是我要达到的目的。

如果你编写JavaScript不太多,这本书仍会为你提供很多有用的信息——特别是复杂度(第2章)、基于事件的架构(第3章),以及调试(第7章)这几章。注意,其余各章也有很多有用的信息,但它们可能不会直接解决你的问题。我遇到的众多难题促使我撰写了这本书——我从之前的错误和努力工作中学到了很多,所以你也应该这样!从头开始养成好习惯,将会让你更富有成效并感到快乐。

非目标读者

遗憾的是,本书并不是适合所有的人。如果你有兴趣学习JavaScript,建议先从其他地方学习一些该语言的基本知识,然后再回到本书。如果你已经能够编写整洁、零Bug的代码,且这些代码有充分的文档和注释,能够自动化构建,且连续运行所有单元测试和集成测试,并能够生成完整的代码覆盖率(code coverage)报告,自动部署到生成环境,这样的话,本书对你可能就没多大用途了。如果不得不进行代码调试的话,可以快速看一下第7章,或者可以看一下第6章,了解一些小技巧。

如果你不经常用JavaScript,现在就可以合上本书了。

自我介绍

我转向JavaScript开发的时间不算长,之前做了很长一段时间的Perl开发。当然,我使用JavaScript也有十年以上了,但从未真正把它当回事儿。以前认为做出“炫酷”的效果和展示其他看似无用的UI技巧仿佛都是很有意义的事情。

Douglas Crockford在Crockford on JavaScript中做出的精彩论述真正打开了我在语言深度和复杂性方面的视野,该视频可以在YouTube的YUI库频道上找到。如果你需要进一步确信,或者你的一些朋友让你对JavaScript感到悲观,看一下这些视频会影响你的心态变化。

我作为前端开发人员在Yahoo! Mail工作了两年半。在我任职期间将它的代码整个重写了。我确信我们团队在重写JavaScript客户端的时候,经历了所有可能遇到的问题、争议、麻烦、灾难,最后取得了胜利。这本书的大部分内容都来自这段经历。

目前,我是Google的一名软件开发测试工程师,我会好好利用这些来之不易的经验教训,希望你也可以。

能从本书学到什么

从这本书中可以学到两点:正确的方法和错误的方法!当然,还有“正确”和“错误”之间的联系。读完本书之后,我希望,不管是自己编还是看别人的代码,你都能知道为什么觉得代码好还是不好。如果你已经可以找出哪些是错误的代码,那么这对你来说是件好事。当我第一眼看到代码的时候,不管是我写的还是别人写的,我都能很快判断出代码是好还是坏:要么是立即理解代码(好感觉),要么是目光呆滞(坏感觉)。能够表达作者代码的问题是非常好的,希望第2章不仅能在好与坏上提供更具体的解释,也能提供与程序作者进行交流的词汇。

为客户端JavaScript编写单元测试可谓是令人怯步。这意味着有很多人都不会编写。这样就不太妙了,本书(以及其他书籍)中详细说明了各种原因。本书提供了编写单元测试入门所需的工具和代码,而不是蜻蜓点水式的介绍。入门是最困难的部分,本书将指导你为客户端JavaScript编写单元测试。

很显然,仅仅编写测试还是不够的,还要运行它们。理想情况下,任何时候都可以在开发环境中运行这些代码,而且它们也可以作为自动构建过程的一部分。那这些测试的代码覆盖率呢?集成测试、性能测试和负载测试呢?所有这些测试的代码覆盖率呢?负责所有这些测试的持续构建环境是怎样的?最后,如何组织代码,以便让所有的测试和自动化变得更加容易而不是更难?如何既在客户端JavaScript又在服务器的JavaScript上运行测试呢?

这些主题以及更多内容(调试及其他)在本书中都会覆盖到,所以准备好开启JavaScript开发世界的狂野之旅吧。本书的重要主题是编写和维护“可测试”的代码。

内容简介

本书将在几个步骤内解决如何编写可测试的代码。首先,我们将研究复杂度(complexity)。接着看架构选择,尝试着限制复杂度和耦合度(coupling)。以此作为基础,在功能层面和应用程序层面上继续测试方面的内容。我们将全面了解代码覆盖率和调试(debugging),然后完成自动化相关的所有内容。在本书最后,大家将更全面地理解“什么是”以及“如何进行”可测试的JavaScript。

第1章 可测试的JavaScript

本书的最重要主题是编写和维护“可测试”的代码。可测试的代码是什么?为什么要努力编写它?如何进行编写?我们将从研究这些问题开始,并且了解一些流行的开发理论以及它们和可测试代码之间有何联系。最后,不管是否跟着进行实战,编写可测试代码的关键都在于让代码保持短小、整洁、简单和松耦合。

第2章 复杂度

复杂度是很多问题的根源,不仅仅是可测试性。这些问题包括可理解性和可维护性,这两个因素是代码质量的关键指标。一些系统和应用程序本质上是复杂的,事实上,大多数应用程序都是很复杂的,但在处理和表达这些复杂性时,有正确的方式也有错误的方式。很显然,将复杂的部分分解成一个个更小、更简单的小块是首要步骤。降低耦合度和扇出(fan-out)是管理复杂度的另外两种方式。在探索可测试的JavaScript时,我们会研究所有这些方法,甚至更多内容。

第3章 基于事件的架构

讨论复杂度之后,我们将深入研究基于事件的架构。该应用程序架构可以极大地降低复杂度和耦合度,同时提供简单的方式将应用程序分解成更小、更自足的片段。不管应用程序是用于服务器端还是客户端,或者(很可能)用于两者,基于事件的架构均可以解决第2章中列举的很多问题。即便该架构不适合作为所有应用程序的总体架构,在整体架构中肯定也会有用到基于事件架构的概念和实践的地方。

第4章 单元测试

关于单元测试有很多争论,测试到底有多重要?单元测试并不能发现所有的错误。像其他工具一样,单元测试是可测试性的一部分。描述代码为“可测试”的,并不意味着这些代码的测试用例是可用的;而是说为这些代码编写测试用例比较简单而已。单元测试之所以特殊,是因为通常这是测试开发人员唯一要编写的。它们还具有侵入性,要求测试代码和程序代码隔离,并且可以独立于应用程序运行。这可能会使单元测试变得有难度,因为在隔离环境下独立运行测试代码是非常困难的。本书很大一部分章节都是讲解如何确保代码能够隔离运行,从而使编写单元测试变得更简单。单元测试无法发现所有的Bug(甚至大多数Bug),但它们所找到的Bug验证了运行单元测试确实是值得的。同样重要的是,测试代码要遵循和即将测试的应用程序代码一样的高标准和原则。

第5章 代码覆盖率

代码覆盖率通常与单元测试有关。代码覆盖率是单元测试的一个很好的衡量标准;然而,我们会发现并非总是如此。代码覆盖率不仅仅适用于单元测试,所有类型的测试,包括集成测试、手工测试、性能测试,都可以受益于代码覆盖率。我们将研究代码覆盖率的优势和劣势,以及如何生成、查看代码覆盖率,并使其变得有意义。

第6章 集成测试、性能测试、负载测试

当然,除了单元测试以外,还有很多其他类型的测试。集成测试、手工测试、性能测试、功能测试以及其他类型的测试,在寻找和挖掘Bug的工作中,都发挥着很重要的作用。不管谁做这些测试工作——开发人员、QA团队,亦或是不知情的用户,不管你喜欢不喜欢,都要完成这些类型的测试。将应用程序作为一个整体进行轻松测试的能力也是至关重要的。模块化功能使测试代码能够与实现的功能更密切相关,这有助于开发人员更快地修复Bug。在这些测试中使用代码覆盖可以快速显示黑盒测试期间执行的代码。大量的基于JavaScript的工具可以让开发人员用于集成测试和性能测试,我们将深入研究其中一些工具,给大家一个直观的展现。

第7章 调试

我们编写的代码,第一次编写时不管看起来多完美,都是不完美的。我们的代码肯定会产生Bug,可能有很多的Bug。我们想到的和意想不到的Bug都有可能会破坏代码。我们的测试、其他人的测试或者用户使用程序时都有可能发现Bug。测试时发现的Bug是最容易解决的,这也是最大化测试的一个很好的理由。用户运行程序时发现的Bug更难以追踪,其结果是,不仅要调试自己的代码,还得调试别人的代码。针对Node.js和浏览器代码这两方面,我将分享一些调试的技巧和窍门。要准备一个好用的调试环境,因为我们要经常用到。

第8章 自动化

最后,对于测试,一遍遍地手工操作不仅不可持续,而且非常无趣。软件编程是世界上手工处理过程最多的工作之一,但测试和软件维护却不一定。运行测试、生成代码覆盖率报告、执行静态分析、精简和压缩代码,以及向生产环境或其他环境部署或回滚代码都应该是自动化过程的一部分。自动化可以确保不管发生什么情况,无论是成功还是失败,它会很快地进行处理,更重要的是在某种程度上可以重复操作。程序代码错了,自动化测试就会失败,生产环境运行也会失败,其他事情也会出错,但这些绝对不会关联到我们的程序代码。这就是现实。关键是要从这些失败(连同你造成的故障)中尽快且不着痕迹地恢复回来。

欢迎反馈

不管你喜欢还是不喜欢本书,都请务必让大家知道。在亚马逊上评论是一种流行的方式,可以分享你的满意度,或者可以在本书的网站( http://oreil.ly/Testable-Java Script )上留下你的评论。

该网站还提供了勘误表链接,请告知我们本书中的文字错误、内容错误以及其他问题。这些勘误提交后将立即可见,我们检查后会给予确认。O’Reilly可以将这些勘误增补在未来的印刷版本和Safari在线图书中,以便给读者提供更好的阅读体验。

小结

编写可测试的代码会让我们自己和效仿我们的人的生活变得更简单。从更少的Bug到更多容易修复的Bug,从容易测试到简单调试,编写可测试的JavaScript是明智之选。

本书将展示通往睿智道路的途径。阅读整本书之后,大家将对编写和维护可测试的JavaScript实际需要方面有非常清楚的了解。但这仅仅是一个开始。我们作为开发人员,必须将这些实践和模式应用到日常工作中。必须抵制住“懒惰”且不编写测试的诱惑,避免走回头路,防止自己或别人来收拾我们的烂摊子。可测试的JavaScript代码将会延续。如果你现在正在编写遗留代码,帮你自己和老板一个忙,开始编写可测试的代码。希望你会发现,这样做并不是很难,而且非常有益,甚至非常有趣!

联系我们

如果您对本书有意见或想提出问题,请联系出版社:

美国:

O’Reilly Media, Inc.

1005 Gravenstein Highway North

Sebastopol, CA 95472

中国:

北京市西城区西直门南大街2号成铭大厦C座807室(100035)

奥莱利技术咨询(北京)有限公司

我们已将本书做成一个网页,我们在那里列出了勘误表、示例和额外信息。可以打开 http://oreil.ly/Testable-JavaScript 访问这个页面。

如需对本书发表评论或提出技术问题,请发送电子邮件至:bookquestions@oreilly.com。

欲获得有关本书、课程、会议及新闻的更多信息,请访问我们的网站: http://www.oreilly.com

在Facebook上找到我们: http://facebook.com/oreilly

在Twitter上追踪我们: http://twitter.com/oreillymedia

在YouTube上关注我们: http://www.youtube.com/oreillymedia

本书约定

本书使用下列排版约定:

斜体(Italic)

表示专业词汇、文件名和文件扩展名。

等宽字体( Constant width

表示广义上的计算机编码,包括变量或函数名、数据库、数据类型、环境变量、语句和关键字。

等宽粗体( Constant width bold

表示应该由用户按照字面引入的命令或其他文本。

等宽斜体( Constant width italic

表示应该由用户替换或取决于上下文的值。

这个图标表示提示、建议或一般说明。

这个图标表示警告或提醒。 C3grgCfIahiS/K/z+DNQdVaqeMgiC6m6WEIJhJNWrDOoai5qbKqfseS2gZBCPJ8d

点击中间区域
呼出菜单
上一章
目录
下一章
×