Web前端测试与集成——Jasmine/Selenium/Protractor/Jenkins的最佳实践(txt+pdf+epub+mobi电子书下载)


发布时间:2020-08-18 04:35:57

点击下载

作者:金鑫,武帅

出版社:清华大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Web前端测试与集成——Jasmine/Selenium/Protractor/Jenkins的最佳实践

Web前端测试与集成——Jasmine/Selenium/Protractor/Jenkins的最佳实践试读:

前言

笔者多年来一直在微软公司从事与Web开发相关的技术工作,帮助客户维护和处理各种Web应用的突发事件,期间走访了大量客户,为他们提供解决方案或指导意见。我们深深感受到近些年来Web技术的快速发展对Web开发人员、测试人员带来的诸多挑战:

Web开发是一个开放的、不断演进的、高速迭代的领域。即便是过去一直被诟病为封闭的微软公司也开始拥抱开源世界,提供.NET Core、TypeScript等多项开源和跨平台技术。这对于过去长期在单一厂商平台上进行项目开发的技术人员而言,就更需要积极主动地学习新技术,接受新挑战,以适应变化,满足业务需求。

基于JavaScript的前端应用规模越来越大,功能越来越复杂,前端测试已经成为保证产品质量的关键因素。同时,由于Web开发存在测试周期短、更新频繁的特点,传统测试人员需要具备一定的开发能力才能充分利用自动化测试工具来提高测试效率。

随着敏捷软件开发方法和DevOps的流行,测试和开发环节之间的界限逐渐变得模糊。传统开发人员需要了解一定的测试方法并具有相应的思维方式,才能设计出良好的测试用例。由于测试和开发环节的融合,无论是开发人员还是测试人员都需要不断提高自身的能力和价值。

本书是笔者在开发测试领域中的实践与经验的总结,希望读者通过对本书的学习,能够掌握Web前端测试的各种技巧,提升自己的能力,迎接新技术的挑战。本书内容

全书共分四个部分,前两部分为金鑫编写,后两部分为武帅编写。

第一部分为基础篇(第1~2章),总览了前端开发测试中的挑战与进行测试转型的方法,以及基于Node.js搭建测试开发基础环境的步骤。

第二部分为单元测试篇(第3~7章),基于单元测试理论深入剖析了Jasmine测试框架的结构与各种使用范式,内容覆盖了所有主流单元测试的技巧。然后,结合gulp、Karma等构建、执行工具对单元测试进行自动化处理。最后以实战的形式演示了AngularJS单元测试最佳实践以及Istanbul代码覆盖率的应用等内容。

第三部分为自动化测试篇(第8~14章),由浅入深地介绍了Selenium各个组件的功能特点和WebDriver在自动化测试中的使用技巧。进而基于Protractor深入介绍了在Node.js环境下,通过JavaScript代码结合WebDriver进行自动化测试,并全面覆盖Chrome、Firefox、IE和Edge等主流浏览器的最佳实践,内容包括页面对象模型、性能测试、数据驱动测试和分布式测试等。

第四部分为集成篇(第15~16章),阐述了基于持续集成技术来实现更快、更可靠的软件交付方法,比较了当前主流CI系统的特点,展示了通过Jenkins与TFS、VSTS和GitHub集成来实现Web应用持续测试的方法。本书适合对象

本书适合所有Web开发人员、测试人员和项目经理做学习、参考之用。本书涉及的示例代码,读者可从网址https://github.com/FrontEndTesting/webtesting-book-demo处下载,供对照学习。致谢

首先,感谢家人对我们利用业余时间编写本书的理解,在漫长的编写过程中始终给予关爱和支持。其次,感谢张量、毛蔚、徐春林和顾洁等微软公司同事的大力鼓励与支持,本书的成书与他们密不可分。

由于笔者学识有限,时间仓促,书中难免出现错误或疏漏,恳请广大读者不吝指正。如果您有什么宝贵意见,请发送邮件至jin_xin2000@outlook.com,我们将不胜感激。基础篇第1章前端开发测试总览

软件的本质是靠良好的质量吸引客户。近些年,随着新技术新模式的出现,企业在交付高质量软件和服务方面,面临着越来越大的压力。正因为如此,对软件产品的质量控制已成为企业生存和发展的核心,几乎所有的企业,在软件开发生命周期里都会持续投入测试力量。

同时,Web开发技术为了适应移动设备和云端服务的应用趋势,发生了很大的变化和演进,各种新标准、新框架、新工具、新理念如雨后春笋般涌现。如何在测试中从容应对各个框架的新特性,全面覆盖各个主流浏览器,为Web测试带来了前所未有的挑战,也迎来了测试自动化转型的契机。

本章将介绍:

Web技术的发展和挑战

传统开发流程的局限性

传统手工测试的局限性

开发模式的转型1.1 Web技术的发展和挑战

Web应用通常分为前端和后端,前端主要在浏览器里显示和采集信息,后端主要处理数据和业务逻辑。传统的Web应用一直偏重后端开发。当用户通过浏览器访问一个网址,这个网址可能是一个存储在服务器上的静态HTML文件,由服务器读取后直接返回。更多的情况是这个网址对应的是一个模板,服务器在用户请求时根据业务逻辑和模板动态拼接成HTML格式的字符串。Web开发人员采用各种后端动态页面技术比如CGI、PHP和ASP等生成这样的字符串。换句话说,所谓的“前端”是由后端服务器动态输出而成的HTML网页。

在这样的传统Web应用中,前端只是充当一个展示层,服务调用、网页跳转流程等都需要由后端服务器处理。每当网页功能有所变化时(即使网页内容只有细微变动),浏览器都需要重新发起一个HTTP请求到服务器,然后由后端服务器重新生成整个页面如图1-1所示。这种方式不但增加服务器的负担,降低响应速度,而且浏览器重复下载整个网页会浪费网络带宽,对于移动应用很不经济。图1-1 传统网页生命周期

而在现代Web应用中,当网页的部分内容需要更新时,运行在浏览器内的JavaScript代码通常会向服务器发送Ajax请求,而服务器端只需输出必要的数据,不需要重新构造整个HTML页面,如图1-2所示。Web前端应用接收到来自服务器的数据后,只需重绘界面上需要变化的部分。这种方式提高了应用的响应速度,改善了用户的使用体验。同时,因为很多用户交互的处理工作可以在浏览器内完成,无需向服务器发送HTTP请求,服务器和浏览器之间交换的数据量大幅减少,所以服务器负荷降低,响应速度也更快了。Web应用渐渐开始拥有和桌面应用一样的响应速度和使用体验。图1-2 现代单页应用(SPA)生命周期

除了网页内容的更新,Web应用一部分业务逻辑的处理也从后端服务器慢慢移到前端,Web前端开发变得越来越重要。而这一切都离不开JavaScript。

JavaScript是1995年由Netscape工程师Brendan Eich花了10天的时间创造出来的,其最初目的是为了满足用户浏览网页时产生的交互需求。可以说,JavaScript是为开发Web应用而诞生的。

自其诞生起的相当长一段时间内,这门在浏览器内执行一些简单的校验和互动的脚本语言常被称之为“玩具语言”,堪称世界上被人误解最深的编程语言。在简洁的外表下,JavaScript其实有着强大的语言特性。它是一种面向对象的动态语言,包含类型、运算符、标准内置对象和方法。其语法来源于Java和C,所以这两种语言的许多语法特性同样适用于JavaScript。

随着Web应用的普及,JavaScript自身也在不断演化以适应更复杂的编程需求,目前JavaScript已经成为Web前端的实际标准,也是最热门的编程语言之一。各种JavaScript前端框架,包括Ember、AngularJS、React和Vue等不断涌现。

正因为基于JavaScript前端应用越来越重要,功能越来越复杂,对前端开发测试带来了极大的挑战。如何保证Web前端应用的正确性和可靠性成为了各大企业关注的问题。

在市场需求的推动下,Web前端应用规模不断扩大,一个应用可能有成千上万行JavaScript代码。这些代码用于执行各种复杂的功能,为用户提供不同的交互体验。为了确保它们能实现预期的功能,因此Web前端应用测试的工作量也需要相应增加。

相对于传统的桌面应用,用户只要重新刷新浏览器就可以获得最新版本的网页,所以Web前端应用具有天生的快速迭代特征,可以频繁地更新和发布。而激烈的商业竞争也使得Web前端应用开发周期缩短,对应的测试周期也非常短。

除了JavaScript代码,Web前端应用还有各种超链接、表单以及图片等信息,需要保证在不同的操作系统和浏览器上可以正确显示网页的内容。1.2 传统开发流程的局限性

在过去二十年或更长的时间中,传统的、非敏捷的瀑布式软件开发模式通常依赖于一个严格的模式化开发流程。通常认为瀑布模型是Winston W.Royce在1970年提出的软件开发模型(虽然他并没有使用瀑布waterfall这个单词)。这种方法源自传统工业生产,严格遵循预先计划的需求、分析、设计、编码、测试和部署的步骤顺序进行,每个步骤都有严格分工,由不同的技术人员分别执行。执行步骤的成果作为衡量进度的途径,例如需求规格、设计文档、测试计划和代码审阅等。但随着各种新兴技术的蓬勃发展,特别是在产品快速迭代的需求背景下,这种传统开发流程的局限性日益明显:1.自由度低,缺乏灵活性

传统模式在项目早期即作出承诺,基于稳定的目标进行阶段性的开发,这种方式自由度低,应对突发情况时缺乏灵活性。众所周知,需求会随着时间而变化,经常出现开发人员努力在设计前完成文档,在编写代码前完成设计,最后却因为需求有变化而不得不完全推倒重来的情况,不仅大量工作被浪费,甚至导致对后期需求的变化难以应变,代价高昂。2.缺陷发现晚,无法及时反馈

传统流程一般在开发阶段接近尾声时才开始测试。虽然在这个阶段进行测试相对容易,但是一些在早期的单元测试中可以轻易发现的缺陷可能要到最后阶段才会发现,增加了被遗漏的风险。

由于缺陷发现得很晚,如果要解决问题,则有可能导致错过发布的最后期限,再加上手工测试效率低下,任何一次代码变更或缺陷修复对产品的影响都无法迅速反馈给开发人员。随着发现的缺陷越来越多,开发人员只会对变更没有信心,失去持续完善的动力。3.协同合作缺失,容易引起团队冲突

传统流程中每个步骤都有严格分工,不同阶段的技术人员与上下游的工种往往沟通较少,甚至对产品本身的理解也存在差异。例如,开发人员和测试人员在思维和工作方式上的不同,使得他们对软件需求和测试场景的表述发生歧义,从而引起双方的沟通问题。而这些理解上的差异如果直到测试阶段才被发现的话,则实在太晚,此时双方的冲突与指责对解决问题没有任何帮助。4.产品质量无法保证

传统流程中常见的一个场景是在项目后期将要交付的阶段,技术人员仓促地从开发环境构建转为脚本配置,而长期在开发环境内依靠手工管理,构建脚本的时候就可能会遇到各种各样的问题,例如接口没有定义、配置文件丢失、组件不工作等。为了解决这些问题,技术人员不得不把主要精力放在软件构建上,结果时间成本越来越高,产品质量无法保证,甚至出现产品无法按时交付的情况。1.3 传统手工测试的局限性

软件测试是在规定的条件下对程序进行操作,以发现程序中的错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。软件测试的目的是希望以最小的代价尽可能多地找出软件中潜在的错误和缺陷。

首先,测试人员会针对开发人员开发的功能写出测试用例,例如表单应该填入的数据,页面单击顺序,以及最后页面期待的显示效果。然后,测试人员会按照用例一步步进行手工检验,如果发现页面行为异常,例如无法打开页面或生成的数据不正确,则会在企业缺陷管理系统中提交缺陷记录,供开发人员进行修正。在开发过程中,如果有新版本编译出来,测试人员需要根据测试用例重新测试,确认是否有新缺陷,或者老缺陷是否已经得到了修正。

长久以来,这种传统的手工测试模式在各大公司广泛应用,并已被证明其能够行之有效地保证产品质量。但伴随着互联网技术的发展,这种传统的测试模式已经显示出越来越多的瓶颈。1.重复性工作,测试质量低

现在的互联网产品开发讲究的是短平快,小步快走,短则两三天,长则一个星期就会发布新版本。在这短短的时间里,测试人员需要把新版本部署到测试环境,更新数据库,然后对所有测试用例进行手工验证。这个过程时间紧迫,工作量大,而且具有很高的机械性和重复性。当测试人员长期工作在重复性的验证事务上时,往往会因为思维惯性而忽略新出现的问题,最后导致不仅测试人员自身缺乏工作热情,而且测试质量更难以保证。2.测试效率低

手工测试天生就决定了它的执行效率很低。测试人员需要根据测试用例逐行逐句阅读,然后在页面上一步步填写表单,再单击按钮提交。这是一个非常烦琐的过程。而遇到复杂的业务流程更是涉及方方面面,作者甚至见过一个多小时都无法完成的测试案例。到了开发后期,可能每天或者每两天就要发布一个版本进行测试。如果一个软件系统的功能点有几千甚至上万个,手工测试将特别耗时和烦琐,不仅消耗了大量的人力,还可能影响到产品的如期发布。3.无法保证覆盖代码全路径

是否有良好的测试覆盖是考核测试成熟度的重要指标,其核心思想是对相同的业务逻辑提供多组甚至几十组输入,全面覆盖到业务中的绝大多数路径,重点考察软件的边界行为。比如某个页面输入框的字符个数在开发中被限制为256个字符,但测试人员很可能漏掉这样的极端输入情况。由于手工测试效率很低,不要说进行几十组数据的测试,就是几组可能都难以实施。另外,有些软件缺陷需要在大量数据或者大量并发用户的情况下才会暴露,很难通过手工测试保证代码的全路径覆盖。4.无法有效兼顾多浏览器、多平台

Web前端的测试环境复杂,兼容性要求高,特别是要同时兼顾多种操作系统,包括Windows、Mac OS和Linux,以及不同的浏览器,包括IE、Edge、Chrome、Firefox等,如果还考虑各个操作系统和浏览器的不同版本,排列组合之后将会是个通过手工测试无法企及的数字。很难想象有哪个公司能够持续投入巨大的人力成本完成如此庞大的手工测试工程。1.4 开发模式的转型

针对传统开发流程和手工测试的局限性,各大企业迫切需要对开发模式进行转型,以应对现代Web应用开发周期短,更新频繁等挑战,同时做到尽早识别测试风险,通过合理的应对策略保证产品质量。1.4.1 敏捷软件开发

敏捷软件开发(Agile Software Development)是一类已经引起广泛关注的软件开发方法,是为应对需求快速变化而发展出的软件开发方法。有多种敏捷开发方法,例如极限编程(Extreme Programming)、精益开发(Lean Software Development)、特征驱动开发(Feature-Driven Development)等,它们有以下共同的特征,如图1-3所示:

迭代式开发。整个开发过程被分为几个迭代周期,每个迭代周期持续的时间一般较短,通常为1~4周。

增量交付。产品是在每个迭代周期结束时被逐步交付使用的,每次交付的都是可以被部署、能给用户带来即时效益和价值的产品。

及时反馈。敏捷软件开发主张用户能够全程参与到整个开发过程中。这使需求变化和用户反馈能被动态管理并及时集成到产品中。

关注软件质量。在开发的整个周期中都关注产品的质量。开发过程中使用的各种工具和方法,例如持续集成、测试自动化、测试驱动开发等都为敏捷项目的整个开发周期提供了可靠的质量保证。图1-3 敏捷软件开发

因为敏捷软件开发拥有更强的灵活性、更短的开发周期、持续反馈等优点,所以敏捷开发被越来越多的软件开发企业和团队所接受。1.更强的灵活性

相对于传统的瀑布模型,敏捷开发尝试以更加灵活的方式让每个开发阶段都并行发生,更强调开发周期内开发团队与客户、开发团队内各个角色之间的紧密协作和有效交流,以便更早发现问题,从而降低改正问题的成本和提高项目成功的几率。2.更短的开发周期

敏捷开发是将一个大项目分为多个相互联系,但也可独立运行的小项目,并分别予以完成。这种模式强调的是尽早将可用的功能交付使用,并在整个项目周期中持续改善和增强。更重要的是,在每个迭代周期中,功能特性被开发和测试,所有发现的问题都被及时修正。这样,开发人员和测试人员之间的时间鸿沟就消失了,因为他们始终在相同的迭代周期中协作。3.持续反馈

敏捷开发短而多的迭代周期为功能调整提供了可能性。用户能够全程参与到整个开发过程中,敏捷团队几乎可以在任何时间满足用户不断变化的需求。4.测试和开发技能的融合

在敏捷软件开发中,测试和开发之间的界限变得模糊。一方面,当敏捷团队配备相对独立的测试人员时,测试人员往往需要有一定的开发能力,才能和开发人员紧密配合完成测试,满足项目进度的要求。另外一方面,当测试角色由开发人员兼任的时候,开发人员需要培养自己良好的测试技能,包括测试用例的设计开发以及执行和结果分析能力。1.4.2 全流程测试

作为保证软件质量手段之一的测试,不应该仅仅局限于软件开发中的某个阶段,它应该贯穿于整个软件开发的全过程。测试开始的时间越早(test early),测试执行越频繁(test often),就可以越早暴露和发现软件系统存在的质量风险。根据测试在软件开发过程中所处的阶段和作用,可分为单元测试、集成测试和端到端测试等。1.单元测试(Unit Test)

软件开发过程中,最基本的测试就是单元测试。这是针对程序单元(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类或者派生类(子类)中的方法。在企业的质量控制体系中,单元测试由开发部门在软件提交测试部门前完成。

单元测试的目标是打破程序单元间的依赖关系,隔离单元并证明这些单个单元是正确的,所以单元测试应该无依赖和隔离。通常在单元测试中,把系统的依赖组件提取出来,用测试替身(Test Double)取而代之,把单元测试把注意力集中放在测试“单元”的逻辑上而不是和第三方系统的交互上,如图1-4所示。常见的依赖组件有网络、数据库、第三方类库和文件系统等。图1-4 单元测试2.集成测试(Integration Test)

即使一个程序单元在隔离状态下运作良好,也并不能确定它们放在一起能正常工作。集成测试是取出应用程序里可以独立运行的组件,通常是一些单元的集合,来测试这些单元作为一个整体的表现,以验证它们能否协调一致地运作(如图1-5)。集成测试一般用于单元测试之后,端到端测试之前。

例如一个常见的集成测试场景是使用数据组件对数据库进行操作的测试。测试人员需要安装并配置好数据库,然后在数据库里插入预先准备好的数据,再执行需要测试的组件,运行完毕后检验数据库里的数据。在这个测试场景中,被测的单元依赖数据库访问模块(例如Microsoft Entity Framework),和一台真实的数据库(外部系统),所以它不是一个单元测试,但是它也没有模拟一个完整的用户真实场景,所以它也不是一个端到端测试。图1-5 集成测试3.端到端测试(End-to-End Test)

端到端测试(缩写为E2E Test)是把产品或服务当作一个整体进行验证。典型的做法是模拟真实的用户场景,通过与系统的需求定义作比较,来发现产品与需求定义不符合或存在矛盾的地方,其最终目的为了发布产品。例如在Web应用程序中,测试人员会启动服务器,打开浏览器,访问被测网页,并操作网页上需要测试的功能,检查浏览器中发生的特定的事件,以确保被测功能可以正常运行。

端到端测试通常由测试部门完成,一般有以下特性:

需要搭建专门的测试环境模拟真实的用户场景,成本较高。

测试用例复杂,运行时间长。

一旦测试发现问题,由于涉及的模块比较多,定位问题难度较高。

端到端测试可以手工完成,也可以编写测试框架和测试代码自动执行。在Web前端应用中,端到端测试通常从用户界面开始,核实用户与应用之间的交互,确保用户界面向用户提供了适当的访问测试对象功能的操作,同时还要确保内部的对象符合预期要求。如果进行手工测试的话,效率低下,无法满足快速迭代的Web前端应用的测试需求,所以迫切需要将Web前端应用的端到端测试自动化。本书第三篇介绍的自动化测试指的就是自动化的端到端测试。1.4.3 让测试自动化

相对于手工测试,测试自动化是把以人为驱动的测试行为转化为机器执行的一种过程。需要避免的误区是,测试自动化并不是要彻底摆脱测试人员,而是一种由人设计机器行为,让机器驱动测试的新模式。

实施测试自动化后,执行者是机器,它可以24小时不停地运行测试代码,充分利用硬件资源,提高测试效率。对于一些手工完成困难或不可能进行的测试,例如测试大量用户同时在线的情况,测试自动化也可以模拟这些用户,提高测试用例的广度。测试自动化对Web前端应用的回归测试效果也非常明显。Web前端应用更新频繁,因为测试自动化的测试用例可以重复使用,保证了测试环境和测试路径的一致性,这不仅可以缩短回归测试的时间,而且很容易发现代码修改引起的回归缺陷。1.4.4 持续集成

测试自动化是进行快速迭代开发的关键一步。然而,如果测试用例执行失败了,是否有一个清晰的工作流程以优先级排序形式标注软件缺陷,反映与商业风险的关联关系,以及列出哪些是急需解决的问题?同时,随着软件开发复杂度的不断提高,团队成员间如何更好地协同工作以确保软件质量,能否通过流程管理解决软件开发的上下游协作?这些已经成为开发过程中不可回避的问题。软件开发急需一种自我管理、自我适应,让开发自动化起来的新模式。

持续集成(Continuous Integration)是一个频繁持续的在团队内进行业务集成,自我反馈完善的软件开发实践。根据Martin Fowler的观点,持续集成要求团队成员经常集成他们的工作,每个人至少每天集成一次。持续集成通过自动化构建,把包括编译、部署、测试、审计和反馈的一组流程用一体化方案驱动起来,整个流程不需要任何用户的人工干预。

持续集成的好处有:

可以及早发现缺陷。持续集成要求每天多次进行集成并执行测试和审查,这可以确保新增代码不会破坏之前的运作。即使出现了回归缺陷,开发人员也可以迅速获得通知,及时修复缺陷。

通过构建自动化过程,减少开发测试人员的重复劳动。

团队成员在任何时间点上提交的代码都可以进行集成,这使得开发团队能随时发布可部署的软件。

持续集成良好的架构可以有效实现分布式团队的协作沟通,让团队成员任何时候都能了解产品的状态,实时地知道当前已经完成了什么功能,还有什么缺陷需要修复。1.4.5 DevOps

在传统的软件开发中,开发、测试和运维都有独立运行的部门。虽然敏捷软件开发模糊了需求、架构、开发、测试之间的界限,但是各部门间仍然存在着信息“鸿沟”。例如运维人员更关注产品的日常运作、可靠性和安全性,而开发人员通常把主要精力放在新功能的开发上。

现代Web应用和移动应用需要频繁而持续的发布。软件产品会被部署到大量的机器集群上,同时要求不中断和破坏当前服务。这些产品在发布前需要通过相关测试,发布后则要求实时监控,支持故障转移、服务降级、快速定位和故障修复。但是在传统的开发运维流程下:

运维人员可能对应用程序内部缺乏了解,难以正确快速地配置环境和发布应用。

开发测试环境和真实生产环境不同,运维人员需要修改部署脚本和配置文件来适应生产环境,这有可能引入新的问题,延缓产品的部署。

开发人员可能对生产环境缺乏了解,从而难以优化代码及配置,造成应用在生产环境下达不到预期运行的效果。

开发人员通常关注的是与业务需求直接相关的功能,而没有考虑监控、故障定位等运维需要的功能。一旦应用在生产环境下发生故障,运维人员无法及时采取措施来恢复运行。

开发人员对配置和环境做了修改后,没有及时与运维人员沟通,经常造成新代码无法在产品环境下运行,产品无法及时发布。

基于以上原因,近年来在软件开发领域一个新概念DevOps(Development和Operations的组合)开始流行。DevOps是一种重视软件开发过程中各个团队之间沟通合作的文化、运动或惯例,通过自动化的流程,使得开发、构建、测试、发布软件能够更加快捷、频繁和可靠,如图1-6所示。图1-6 DevOps

DevOps不是一种技术或工具,而是一种文化转变,它鼓励团队合作,以便更快地构建可靠性更高、质量更好的软件:

运维人员要懂得产品的架构与功能,而不仅仅是管理员手册。

开发测试人员要懂实际的运维,包括从实际部署、上线流程,到故障的定位与解决。

运维所需的功能甚至基础设施要成为产品非功能性需求的一部分。

产品交付与运维需要集成到整个软件生命周期中。

从瀑布模型到敏捷开发,敏捷开发到持续集成,持续集成到DevOps,不管流程如何制定,目的都是相同的:在不牺牲质量的情况下更快地交付产品。1.5 本书目标

对Web前端应用进行测试之所以困难,一方面是因为代码运行的环境几乎无法控制,各种类型的操作系统,各种版本的操作系统,各种类型的浏览器,各种版本的浏览器,各种语言、插件、扩展,各种前端框架都交织在一起;另一方面其快速迭代的特性使得测试周期短,测试质量难以保证。

同时,Web前端应用开发模式的转型也给开发人员和测试人员带来了新的挑战。开发人员需要了解一定的测试方法和思维才能设计出良好的测试用例。而测试人员需要一定的技术能力才能充分利用自动化测试工具提高测试效率。由于测试和开发进行了融合,所以无论是开发人员还是测试人员,都需要不断提高自身的能力和价值。

本书目标是通过介绍:

单元测试

自动化测试

持续集成

使读者了解如何利用各种工具框架编写测试用例,对Web前端应用进行高效测试,并最终提高软件产品的质量。第2章搭建测试基础环境

编写和运行本书的示例代码需要相应的工具和运行环境。本章将介绍测试基础环境的搭建和常用工具。

本章将介绍:

JavaScript的运行环境Node.js

软件包管理系统Node Package Manager(npm)

代码编辑器(Visual Studio Code)2.1 JavaScript的运行环境Node.js2.1.1 什么是Node.js

Node.js这个名字很容易让人以为是一个JavaScript的应用或类库。实际上,Node.js是一个JavaScript的运行环境,主要采用C++语言编写而成。

要了解Node.js,首先要了解什么是JavaScript引擎。JavaScript是一门高级语言,计算机并不能直接执行,所以需要使用所谓的引擎来将其转换成计算机能理解的机器语言。最初,JavaScript主要运行在浏览器里,浏览器中的JavaScript引擎负责解析和执行网页中的JavaScript代码,并提供代码执行的运行环境,例如内存管理(内存分配,垃圾回收)、即时编译(Just-in-time Compilation)、类型系统(Type System)等服务。

C#和JavaScript的运行环境对比,如表2-1所示。表2-1 C#和JavaScript的运行环境比较

如果能把JavaScript引擎从浏览器中独立出来,那么JavaScript代码就可以被移植到浏览器以外的环境中执行,和其他高级编程语言一样做网页交互以外的事情,从而大大拓宽了JavaScript的应用范围。

2008年,Google公司为Chrome浏览器开发了开源JavaScript引擎V8。Node.js的诞生可以说很大程度上归功于V8引擎的出现。当时其他JavaScript引擎对JavaScript代码进行解释执行,性能较差;而V8使用即时编译,在代码执行前将JavaScript编译成二进制机器码再执行,极大地改善了JavaScript程序的性能,使得JavaScript程序在V8引擎下的运行速度可以媲美二进制程序。

除了能够大幅提升JavaScript性能,V8引擎也可以作为独立的模块,由开发者在自己的C++程序中“嵌入”V8引擎,从而高效地编译JavaScript。

2009年,Ryan Dahl创建了基于V8引擎的Node.js项目(后来得到Joyent公司的资助)。Node.js是一个开源的、跨平台的JavaScript运行环境,最初发布在Linux平台上,直到2011年7月,在微软的支持下才发布了Windows版本。它对V8引擎进行了封装,并提供很多系统级的接口调用,如文件操作、网络编程等,可以用JavaScript编写响应速度快、易于扩展的网络应用。

Ryan Dahl创造Node.js的目的是为了实现高性能的Web服务器。在传统服务器软件开发中,并发的请求处理一直是个大问题。传统服务器模型通常为每一个请求生成一个新线程或新进程,一方面服务器创建新线程/新进程会造成延时,另外一方面,新线程/新进程会消耗额外的内存,浪费资源。在这种传统模型中,如果应用程序的某个任务很耗时,涉及到大量I/O操作(比如访问文件),对应的线程就处于一种不占用CPU,而只是等待响应的状态,直到数据传输完成。但由于等待期间该线程/进程依然占用着资源,当大量并发请求到达时,就会产生阻塞,造成服务器瓶颈。

Node.js以事件驱动为核心,使用非阻塞I/O模型,它为每个连接发出(emit)一个事件(event),放进事件队列当中,而不是为每个连接生成一个新的线程/进程。理论上,只要有用户请求连接,Node.js都可以进行响应。同时,该I/O模型提供的绝大多数应用编程接口都是基于事件的、异步的风格。开发人员根据自己的业务逻辑需要在相应的事件上注册回调函数,这些回调函数在相应事件触发后被调用。例如,当应用程序发生一个I/O操作(比如访问文件)时,Node.js的主线程可以继续执行,而无需等待这个操作完成;等到这个I/O操作完成,相应事件被触发的,回调函数被再执行。这使得Node.js在相对较低的系统资源消耗下具有高性能与出众的高并发能力。

JavaScript是Node.js的天然载体语言,因为它允许使用匿名函数和闭包,非常适合事件驱动和异步编程。并且JavaScript作为一门编程语言自身不带I/O功能(一般的编程语言都带一个I/O模块,很不幸的是这个模块通常是同步的,而JavaScript最初是在浏览器内运行,浏览器负责I/O,所以JavaScript没有这个历史包袱)。

Node.js自诞生以来发展迅速,社区活跃,吸引全世界开发者为其贡献了大量的工具、模块和框架。很多企业也逐渐开始采用Node.js开发项目。

本书虽然不是介绍如何利用Node.js开发高性能Web应用的,但是示例所需的很多工具和框架依赖于Node.js的JavaScript运行环境,所以需要安装Node.js。2.1.2 Node.js的版本发展

初次接触Node.js的读者,可能会对Node.js的版本产生困惑。你可能看到过v0.8.x、v0.10.x、v0.11.x、v0.12.x等版本,然后版本号突然变成4.x.x(本书编写时最新版本号是6.8.1),这和Node.js的发展历史有很大的关系。

Ryan Dahl创建Node.js时,采用的是和Linux内核相同的奇偶版本模式,即版本由3个整数组成,格式为“A.B.C”,A代表主版本号,B代表次版本号,C代表较小的末版本号。只有在内核发生很大变化时,A才变化。C代表一些缺陷修复、安全更新、新特性和驱动的次数。而通过数字B来判断产品是否稳定,偶数的B代表稳定版,奇数的B代表不稳定的开发版。

2010年,Joyent公司雇用了Ryan Dahl并让其专职负责Node.js的发展,在此同时,还得到了Node的品牌使用权。Joyent的Node.js继续使用奇偶版本模式,比如v0.8.x、v0.10.x、v0.11.x、v0.12.x。

2012年,Ryan Dahl离开了Node.js的项目负责岗位并淡出了公众视野。Ryan Dahl离开后,Node.js开源社区的贡献者和Joyent发布的更新数量都在不断缩减。

2014年12月,由于对Joyent公司垄断Node.js项目以及该项目进展缓慢的不满,一部分核心开发者离开了Node.js,创造了io.js项目。这是一个更开放、更新更频繁的Node.js版本。io.js的版本策略是语义化版本(Semantic Versioning),使用3个整数表示向后兼容的程度,格式为MAJOR.MINOR.PATCH:

MAJOR(主要)。此位版本号表示此版本号和前一个版本号在API上不兼容,如3.0.0的API可能不兼容2.0.0的请求。

MINOR(次要)。此位版本号表示新添加了一个功能,且保持旧功能完全向后兼容。

PATCH(补丁)。此位版本号表示修复了以前功能的Bug,且完全向后兼容。

2015年2月,Joyent公司宣布放弃对Node.js项目的控制,将其转交给新成立的开放性质的Node.js基金会。

2015年9月,io.js项目宣布回归Node.js。Joyent的Node.js v0.12.7和io.js的v3.3.1合并成新的Node v4.0,这是为了防止与后续Joyent的Node.js 0.x.x维护计划和任何现有的io.js版本发生冲突。这次合并之后,整个项目采用语义化版本编号策略。所以4.0.0是重生后的Node.js真正的“1.0”版本。

注意,如果读者使用的是Visual Studio 2015,安装时可能选装Joyent Node.js,如图2-1所示,但是它的版本是v0.12.2。所以本书建议采用从官网下载安装包的方式安装Node.js。图2-1 Visual Studio 2015安装界面2.1.3 安装Node.js

Node.js可以在主流操作系统上运行,包括Windows、Linux和Mac OS。Node.js安装包及源码下载地址为https://nodejs.org/en/download/,如图2-2所示。图2-2 Node.js下载页面

用户可以根据不同操作系统选择所需要的Node.js安装包。本书示例基于Windows 10操作系统,采用Node.js v4.6.0 LTS(长期支持版本)的64位Windows安装包(.msi)来安装Node.js,步骤如下:(1)双击下载后的安装包node-v4.6.0-x64.msi,出现欢迎界面,单击Next按钮。(2)勾选接受最终用户许可协议选项,单击Next按钮,如图2-3所示。图2-3 Node.js用户许可协议(3)使用默认安装目录和默认安装方式安装Node.js。安装完毕后单击Finish按钮退出安装向导,如图2-4所示。图2-4 Node.js安装完成

打开命令控制台,运行以下命令检查当前Node.js的版本:C:\>node --versionv4.6.0

为了验证Node.js能否正常工作,可以直接输入node,按回车键。C:\>node>

如果Node.js成功安装,此时就进入了Node.js的命令行模式,可以直接输入JavaScript命令,按回车键执行。例如,执行命令console.log('Hello World!'):C:\>node>console.log('Hello World')Hello World!Undefined采用以下两种方法可以退出Node.js的命令行模式:按两次快捷键Ctrl+C。输入.exit,按回车键。2.2 软件包管理系统Node Package Manager(npm)

依赖关系是软件包管理的一个重要方面。每个软件包都有可能依赖其他的软件包,例如软件包A需要软件包B,而软件包B需要软件包C。通常这些软件包并不具备自动安装所依赖的软件包的功能,当用户安装软件包A时,他需要预先手工安装软件包B和C。如果依赖关系复杂的话,则用户将不得不花大量精力去处理这些软件包之间的依赖关系。

软件包管理系统一般能够从软件源处自动下载所依赖的软件包并安装,解决软件包之间的依赖关系,所以软件包管理系统在各种系统软件和应用软件的安装管理中均有广泛应用。例如,微软.Net开发平台上的软件包管理系统是NuGet。

Node Package Manager(npm)顾名思义是Node.js的软件包管理系统,完全以JavaScript编写,支持跨平台,由Isaac Z.Schlueter在2010年创建,主要功能包括:

一个在线仓库(https://registry.npmjs.org),允许开发人员将自己编写的JavaScript软件包注册并上传到这个在线仓库供下载使用。

命令行工具用于解决JavaScript软件包的依赖关系,例如从在线仓库中搜索、下载、安装、卸载、更新所需要的JavaScript软件包,并将它们整合到自己的项目中。在本书中npm主要指这个命令行工具。2.2.1 安装和更新npm

npm不需要单独安装,安装Node.js时会一并安装npm,它是Node.js的默认软件包管理工具。由于npm更新频繁,Node.js附带的npm可能不是最新版本,用户可以在命令控制台执行以下命令将其更新到最新版本。npm install npm@latest -g

运行npm命令可查看各种信息。(1)查看npm的版本,命令如下:npm -v(2)查看npm命令列表,命令如下:npm help(3)查看npm的配置,命令如下:npm config list -l2.2.2 package.json

Node.js项目的根目录下一般会有一个package.json文件。这个文件定义了当前项目的属性,包括项目运行时所依赖的软件包。

package.json常用属性如表2-2所示。表2-2 package.json常用属性

以下是一个基本的package.json文件。在package.json文件中,只有name和version字段是必要的,其他字段都是可选的。{ "name": "myproj", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": { "jquery": "^3.1.1", }, "devDependencies": { "karma": "^1.3.0" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC"}

以上package.json示例定义了项目的入口文件为index.js。当其他Node.js应用通过require引用这个软件包时,index.js文件将被调用。

package.json文件可以手工编写,也可以通过执行npm init命令自动生成。该命令采用互动方式,要求用户回答一些问题,然后在当前目录下生成一个基本的package.json文件。所有问题之中,只有项目名称(name)和项目版本(version)是必填的。

本书的示例是Web前端程序,不是Node.js项目,那为什么要介绍package.json文件呢?2.2.3 安装软件包

本书介绍package.json文件,是因为它可以定义项目运行时所依赖的软件包(dependencies)和开发环境所依赖的软件包(devDependencies)。有了package.json文件,开发人员可以在项目根目录下直接执行npm install命令,该命令会根据package.json文件从在线仓库中下载dependencies和devDependencies中列出的软件包,安装到当前目录的node_modules子目录中,这样就解决了Web前端程序的依赖问题。package.json文件里的dependencies和devDependencies字段列出的软件包采用语义化版本,npm install会根据这个信息下载相应版本的软件包,例如:"devDependencies": { "karma": "^1.3.0"},其中,^前缀表示与指定版本兼容,最左边的主要版本不变,但是次要版本和补丁版本可以是更高的版本。例如“^1.3.0”表示这个项目支持>=1.3.0但是<2.0.0版本的Karma。npm install会下载符合条件的最新版本的软件包。如果想要了解更多有关npm的语义化版本信息,可以参考https://docs.npmjs.com/misc/semver。

如果只安装dependencies字段里列出的软件包,可以执行以下命令:npm install --production不要将node_modules目录提交到源代码管理系统中。基于package.json,只需要执行命令npm install就可以恢复项目的开发和运行环境。

如果项目依赖的软件包没有被定义在package.json文件里,那么这些软件包需要单独安装。安装软件包的形式分两种:本地安装与全局安装。1.本地安装

本地安装软件包的命令是:npm install

本地安装的软件包会被下载到当前所在目录(通常是当前项目的根目录)的node_modules子目录中,适用于安装一些JavaScript库和框架。这些库和框架会被当前项目所引用。

如果使用--save参数,npm命令会将软件包信息写入package.json文件的dependencies字段中,这通常用于安装项目运行所需要的软件包。npm install --save

如果使用--save-dev参数,npm命令会将软件包信息写入package.json文件的devDependencies字段中。这样的软件包通常仅被用于开发环境。npm install --save-dev建议在每个项目的根目录中都创建一个package.json文件。在使用npm安装软件包时,建议使用--save或--save-dev参数。

例如当前项目需要karma软件包,c:\myproj是当前项目的根目录,打开命令控制台,将当前目录切换到c:\myproj,执行以下命令:C:\>cd c:\myproj

然后执行以下命令(因为karma用于测试JavaScript代码,属于开发环境需要的软件包,所以使用--save-dev参数):C:\myproj>npm install karma --save-dev

安装完毕后会输出以下信息:C:\myproj>npm install karma --save-devmyproj@1.0.0 C:\myproj`-- karma@1.3.0 +-- bluebird@3.4.6 +-- body-parser@1.15.2 | +-- bytes@2.4.0 | +-- content-type@1.0.2 | +-- debug@2.2.0 | | `-- ms@0.7.1 | +-- depd@1.1.0…

以上输出信息的简单说明如下:

karma@1.3.0,当前安装的软件包,版本为1.3.0。

bluebird@3.4.6,karma依赖的软件包,版本为3.4.6。

body-parser@1.15.2,karma依赖的软件包,版本为1.15.2。body-parser依赖的软件包在这个树状结构的下一层。

karma软件包被安装在node_modules目录下的karma子目录中。除karma目录外,还有其他依赖软件包的目录,这些都是NPM根据软件包的依赖关系自动下载的,如图2-5所示。图2-5 node_modules目录2.全局安装

全局安装指的是将软件包安装到一个全局安装目录中,这样各个项目都可以使用这些软件包。一般来说,全局安装适用于各种Node.js工具。例如npm本身就是一个全局安装的软件包,所以可以在命令控制台里直接执行npm。

全局安装软件包的命令是:npm install -g

例如,本书示例需要使用gulp工具,就可以采用全局安装的方式,使各个项目都可以使用gulp工具。具体命令如下:npm install -g gulp

运行以上命令后,gulp工具被安装到目录{prefix}\node_modules\中。在作者的计算机上,{prefix}是C:\Users\demouser\AppData\Roaming\npm目录。以下命令会显示当前{prefix}的值:npm config get prefix

全局安装目录也可以通过以下命令进行修改:npm config set prefix=c:\global_packages

修改全局安装目录后,软件包会被安装到c:\global_packages\node_modules。修改完全局安装目录后,请将新的目录添加到Windows的环境变量PATH中,并将旧目录从PATH环境变量中删除。以后就可以在命令控制台中直接执行全局安装的工具了。2.2.4 列出已安装的软件包

npm list命令以树型结构列出当前项目安装的所有软件包,以及它们依赖的软件包。具体命令如下:npm list

如果加上一个参数-g或--global就可以列出全局安装的软件包和它们依赖的软件包。具体命令如下:npm list -g

如果不想输出所依赖软件包的信息,可以添加--depth参数。具体命令如下:npm list -g --depth=0

运行以上命令将得到如下结果:C:\>npm list -g --depth=0c:\global_packages+-- gulp@3.9.1`-- npm@3.10.82.3 代码编辑器(Visual Studio Code)

一款好用的编辑器能够显著提升开发人员的工作效率。本书示例使用的是微软开发的跨平台(支持Windows、Linux和Mac OS操作系统)开源代码编辑器Visual Studio Code。

和全功能的集成开发环境Visual Studio不同,Visual Studio Code的定位是一个轻量但又功能强大的代码编辑器,可帮助开发人员进行快速编码、编译和调试。

Visual Studio Code来源于微软的一款使用HTML、CSS和JavaScript开发的在线编辑器Monaco(用于Visual Studio Online、OneDrive等),在这个基础上利用基于io.js和Chromium的开源框架Electron进行包装(Electron可以让用户使用JavaScript调用操作系统的原生API来创造跨平台桌面应用),成为一款跨平台的桌面代码编辑器。

作为代码编辑器,Visual Studio Code支持多种编程语言,其中原生支持JavaScript、TypeScript、CSS和HTML。开发人员可以通过VS Code Marketplace下载扩展插件获得其他编程语言的支持。

Visual Studio Code支持调试。原生调试功能限于Node.js环境,可以调试JavaScript、TypeScript和其他能够被转译为JavaScript的编程语言。其他运行环境和编程语言(例如PHP、Ruby、Go、C#、Python)的调试支持也可以从VS Code Marketplace下载扩展插件获得。

Visual Studio Code内置了Git版本控制功能,支持用户自定义配置,例如改变主题颜色、键盘快捷方式、编辑器属性等。2.3.1 安装Visual Studio Code

安装Visual Studio Code的方法是,先从官网https://code.visualstudio.com/下载安装文件包,双击VSCodeSetup-stable.exe启动安装程序,如图2-6所示。

然后单击Next,按安装向导指示使用默认设置安装即可。安装结束后可以直接启动Visual Studio Code,如图2-7所示。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载