异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
Cucumber是一个命令行工具。运行时它会从普通语言编写的称为特性(feature)的文本文件中读取你的规格说明,解析需要测试的场景(scenario),然后针对你的系统运行这些场景以达到测试的目的。
Cucumber测试同传统的规格说明文档一样能被利益相关人阅读和编写,然而其独特的优点在于,你可以在任何时刻给他们一台计算机让测试执行,结果会告诉你测试有多准确。在实际情况中,这意味着你的文档不再是一种写完后就慢慢过期的东西,而成为一种能随时反映项目真实状态的活的东西。
Fiddler是一种流行的Web调试代理。它功能强大,界面友好,简单易用,无论对开发人员或者测试人员来说,都是非常有用的工具。
Fiddler可以捕获所有的本地数据流,而且支持很多过滤器(filter)。这一特性使得用户既可以隐藏不感兴趣的数据流,也可以高亮显示(使用颜色或字体选项)感兴趣的数据流。过滤器的过滤条件可以是数据流的来源(如特定的客户端进程),也可以是数据流本身的某些特征(如该数据流所绑定的主机名或服务器返回内容的类型)。
首先,坦白地说,开发Fiddler Web调试器时,并没有什么伟大的愿景或期望—要做一个全世界最受欢迎的调试代理。它只是由具体需求触发,应运而生。我从未打算构建一个功能如此灵活而强大的复杂平台。因为它的复杂性,我不得不花费九个月的时间来写这本书,为的就是介绍如何充分利用Fiddler。
配置管理(CM,Configuration Management)在任何开发工作中都起着非常关键的作用。我从事配置管理的实施和支持工作已经超过25年,本书中将讨论的大部分内容都直接来自于个人的经验。我实施并支持过各种配置管理的实践方法并达到这样一种状态——如果建立的过程或自动化没有按照预期般运作的话,我经常会在半夜里被惊醒。
构建工程最佳实践可以提高整个开发团队的效率,产出更高质量的代码。构建过程应该自动化,可回溯,快速,并且尽可能地简单。当遇到项目使用的技术十分复杂时,应该把构建分成几个可控制的部分。如果构建过程中可以自我检测错误,开发活动就会更有效。
在配置管理的大背景下,构建工程逐渐被人们所了解。最近几年构建工程的日益成熟和受到的关注更加速了它的发展。也许当初公司是为了合规的要求才实现的构建工程,比如想实施IT访问控制。但是除了通过认证或者审计,应该看得更远一点。
持续集成是一个相当流行的软件开发实践。现在,人们时常把持续集成和敏捷开发联系到一起。实际上,即使开发团队使用的不是敏捷开发的过程, 持续集成在他们之间也已经非常流行。另外一个很明显的现象是很多研发团队并不需要签入构建(commit build, check-in build),也就是说并不需要每次有代码签入代码库都要立刻触发一个构建。
实施构建工程最佳实践是一项非常具有挑战性的工作。构建工程师可以选择有益于公司的实践;也可以选择最好的工具去建立可重复的构建,实施持续集成。但是实际工作远不止此,构建工程部门还需要为开发团队提供培训和技术支持。
构建工程师常常忙于建立可重复的构建流程。成功完成一个项目后就会更忙,因为有更多的项目需要协助。开发技术不是永恒不变的,其他专业技术人员可以很快地学习和使用新技术架构。而构建工程师接触新技术并能立刻应用到构建方面的情况却不是很多。
构建工程师通常需要有软件开发的背景,扎实的技术知识,一定程度的编程能力(包括 Perl, Python, Shell 和 XML),可以建立可靠、可重复的构建过程。工作中很难找到一个合适的构建工程师,因为具有很强开发背景的专业技术人员通常都更愿意做项目而不愿意写构建系统。
构建工程师不仅要随时关注构建的过程,还要关注开发人员调试构建、解决构建问题的方法,甚至是每一步的操作。有这样一个实例,我观察到每当构建失败,开发人员就会查看某个特定的 JAR 文件是否被创建,是否包含在了 WAR 文件当中。
快速可靠地构建平台需要资金的支持,包括购买相应的硬件和软件系统。许多公司发现采用虚拟化技术提供构建和测试环境效率高且非常划算。除此之外,一定要为将来的培训和必要的资源留下预算。构建工程非常需要充足的资金支持。
目前有很多好的构建工具,也有很多相关的最佳实践教程。这些教程可以指导你建立一个可靠、可扩展的构建流程。这里将会讨论一些工具和最佳实践,你可以有选择性地实施其中一些来支持公司的开发工作。目前软件开发中主要有几类比较流行的构建工具。
成功的构建工程都包含如下职责:首先构建的依赖关系易被理解且受控,基线是可识别的,在此基础上可重复地生成构建。每次构建都是针对配置项的活动。构建包含配置项,并且可产生新的配置项。几乎构建中的任何东西都可以被认为是配置项。
首先就是要研究现有项目的构建过程。有时你会发现研发团队已经写好了构建脚本,可能采用的是Ant, Maven 或者Make。通常情况下,现有的构建过程只能部署产品到研发的测试环境中。构建工程师就要在此基础上进行修改,从而支持QA环境和产品环境。
构建工程有时候是项很令人头疼的工作。比如,有的公司开发团队甚至经常长时间无法获得一个可靠的可执行文件。原因可能有以下几个:第一个原因是没有实行可靠的源代码管理实践。这导致了我们不知道怎么能得到一份版本相同的源代码。第二个原因是构建过程太复杂且不可靠。第三个原因是不支持新的构建需求。
源代码管理是配置管理最佳实践的核心。在保护源代码的同时,利用配置管理工具和规范可提高工作效率和产品质量。在选择源代码管理工具和购买某些源代码管理功能时,一定要小心行事。培训和定义良好的使用模型有助于确保配置管理职能有效地执行且容易被接受。
很多专业技术人员是源代码管理工具和规范的高级用户,而另外一些人仅仅期望以最少的精力把工作做完就可以了。团队中的每个人并非都要成为一个源代码管理工具专家,但应该认可和授权那些在源代码管理方面想提高的用户。
实施配置管理最佳实践,很重要的一条就是建立一个团队来帮助实施、支持和管理源代码管理职能和其他将要讨论的五个方面。把配置管理团队的服务作为一种共享资源非常重要,这会给整个开发团队带来更多的价值。这个小组通常被称为发布管理服务(RMS, Release Management Services),最初仅仅是做发布代码的工作。
在采用任何一个源代码管理工具之前,都要考虑部署、培训和支持团队将要花费的时间和精力,还要考虑任何可能阻碍进展的风险。例如,拒绝参加培训的开发人员是一个严重的风险因素。在尝试部署任何配置管理工具之前都要重视这种现象。
在公司内,设置专人定义整个团队如何使用源代码管理工具是非常重要的。在理想的情况下,这个人从整个团队那里得到信息,明确地定义出使用模型是决定选择使用哪个工具非常重要的部分。可以预料的是这是一个反复迭代的过程,并且肯定会有很多不同的意见。
在选择源代码管理工具时,还要考虑供应商或者第三方机构是否有系统性的培训。有时我更倾向于那些非供应商提供的培训,因为我希望从一个没有偏见的角度去了解如何有效地使用工具,尤其是涉及工具缺陷和限制等。供应商有时认为培训只是销售过程的延伸,所以他们的培训总有种推销的味道,不能让人从公正的角度去看工具。
曾经在一家公司任职,管理层要求我让所有人员都用上一个业界最强大的源代码管理工具。公司已经为这个工具付了一大笔钱,但是使用率却在下降。随着时间的推移,我成功地支持了分布在世界各地的700个开发人员使用的1500个代码库。
在地理分布式的工作环境中,全球性软件开发的挑战是能够协调好工作和使变更受控。对于同一房间里坐在对面的两个人,协调好工作和使变更受控都不是件很容易的事情,而对于那些身处不同国家,工作在不同时区,有着不同语言、文化、追求的团队来说,这就更难了。
跟踪缺陷和需求变更是一项非常重要的功能,这可以告诉你为什么做了一个特定的变更或查明是否不经意地错过了一个必须做的变更。在第13章“建立IT控制及合规”中,我们会讨论可跟踪性的重要性。一些配置管理工具已经集成了缺陷或者需求跟踪的功能;另外一些工具需要一点编程的工作,通过应用程序接口(API)和外部工具集成到一起。
许多开发者认为源代码管理就是简单地从源代码管理工具中(一个代码库)签入和签出代码。就像大多数人认为的那样,多年前一些比较老的版本管理系统的确是这个样子。如今,虽然大多数配置管理代码库具备的可靠性和功能性不同,但都能支持变更,确保所有变更的安全。
实施源代码管理最好的切入点是确定源代码管理的目标和需求。我曾经工作过的一些公司,有的是集中全公司的资源来做好配置管理;而有的则是源代码管理几乎处于自我管理的状态。大多数公司开始做都是先评估其现有的做法,如确保代码安全、控制变更、建立基线和发布,包括修复补丁。
源代码管理之所以如此重要是因为既要保证资产是安全的,又要允许通过受控、可靠的配置库对其进行访问。源代码管理提供了管理源代码和把工件组装成系统的工具和方法。在配置管理术语中,我们称其为配置项(CI, Configuration Item)。
正则表达式在过去十多年间越来越普及。如今,所有常用的编程语言都会包含一个强大的正则表达式函数库,甚至在语言本身就内嵌了对于正则表达式的支持。许多开发人员都会利用这些正则表达式的功能,在应用程序中为用户提供使用正则表达式对其数据进行查找或者过滤的能力。正则表达式已经无处不在。
上下文(context)这个术语,指的是正则表达式所应用于的目标文本。总共存在3种上下文:正则匹配之前的目标文本、正则匹配之后的目标文本以及整个目标文本。在匹配之前的文本有时候被称作左上下文(left context),而匹配之后的文本则相应地被称作右上下文(right context)。
实例2.10讲解了在正则表达式中如何使用捕获分组来多次匹配相同的文本。在正则表达式中,每个捕获分组匹配到的文本在每次成功匹配之后都是可用的。你可以把部分或者所有捕获分组中的文本按照任意顺序甚至多次插入到替代文本中。
把整个正则匹配重新插回到替代文本中,是在匹配文本之前、之后或者两边,甚至是在匹配文本的多个副本之间插入新文本的一种比较容易的方式。除Python之外,你都不必在正则表达式中添加任何捕获分组,就能够在替换中使用整个匹配结果。
在这个例子中的百分号(%)和星号(*)总是字面字符,然而一个前导的反斜杠也可能会被当作一个转义字符,而不是一个字面上的反斜杠。«$1»和/或«\1»是指向一个捕获分组的反向引用。实例2.21中会讲解哪些流派对于反向引用会使用哪种语法。
打开宽松排列模式会产生两个效果。首先,它会把位于字符组之外的井号(#)转变成一个元字符。井号会作为一个注释的开始,该注释的结尾是一行的结束或者该正则表达式的结束(取二者中先到的那个)。井号以及其后的所有内容都会被正则表达式引擎直接忽略。
这里的括号、问号和竖线都是属于条件判断语法的一部分。它们在这里并不具有平时的含义。你可以在‹then›和‹else›部分中使用任意种类的正则表达式。唯一的限制是如果想要在其中一个部分之内使用选择分支,就必须使用分组来把它们包到一起。因为在条件判断中只允许直接出现一条竖线。
匹配的过程则是完全相同的。引擎会在进入否定型顺序环视的时候保存当前匹配进程,然后试图正常地匹配顺序环视中的正则表达式。如果这个子表达式匹配的话,那么否定型顺序环视会失败,而正则引擎会进行回溯。如果这个子表达式不能匹配的话,那么引擎会恢复保存的匹配进程,然后继续处理正则表达式的剩余部分。
如果我们对正则表达式中所有其他的‹.*?›都做同样的操作,那么它们就都不会再继续扩展。虽然在正则表达式中还是存在7个惰性点号,但是它们永远也不会产生交叉。这样就会把正则表达式的复杂度降低到O(n),它与目标文本的长度之间是线性关系。而正则表达式的效率不可能比这更高了。
我们描述占有量词不会去记住回溯位置,而固化分组则会把回溯位置丢弃。这样会更容易理解匹配过程,但是读者也不要太在意这里的区别,因为很可能在你所使用的正则流派中根本不存在这样的区别。在许多流派中,‹x++›仅仅是‹(?>x+)›语法上的简写,而二者的实现则完全是一模一样的。
懒惰量词也会进行回溯,但却是从不同的方向进行的。一个懒惰量词会重复尽可能少的次数,然后保存一个回溯位置,并且允许正则表达式继续。如果后面的正则表达式匹配失败了,那么引擎会进行回溯,此时懒惰量词会再重复一次。
如果我们使用可变次数重复,并把n设置为0,事实上我们就是把在该量词之前的记号变成了可选的。‹h{0,1}›匹配一次‹h›或者根本不存在。如果不存在h,那么‹h{0,1}›会得到一个长度为0的匹配。如果你使用‹h{0,1}›来作为一个正则表达式,那么它会在目标文本中每个不是h的字符之前找到一个长度为0的匹配。
可能是基于.NET的流行超过了Python的原因,.NET语法似乎成为了其他正则库开发人员更乐于接受的语法。Perl 5.10及更新版本使用的是它,而在Ruby 1.9的Oniguruma引擎中也是如此。
下一个记号是连字符,它会按照字面进行匹配。接着就遇到了反向引用。正则引擎会检查第一个捕获分组的内容:08。然后引擎会试图按照字面来匹配这个文本。如果该正则表达式是不区分大小写的,那么捕获分组也会按照这种方式进行匹配。
创建一个正则表达式,使之匹配yyyy-mm-dd格式的任意日期,并且分别捕获年、月和日。目标是在处理匹配的代码中可以更容易处理这些分别捕获的值。你可以假设目标文本中的所有日期都是合法的。正则表达式不必要考虑去掉像9999-99-99这样的非法数据,因为它们根本不可能出现在目标文本中。
本书中的所有正则表达式流派都会使用正则制导的引擎。正则表达式依赖于这台引擎来工作。这里所说的正则制导1FF的含义是,在目标文本中的每个字符位置会首先匹配该正则表达式的所有可能排列,然后才会到下一个字符位置进行匹配尝试。
Unicode字母表(script)支持要求PCRE 6.5或者更新版本,而且PCRE必须使用UTF-8支持进行编译。在PHP中,需要使用/u模式修饰符来打开UTF-8支持。.NET、JavaScript和Python不支持Unicode属性。
如果要使用正则表达式来进行“只匹配完整单词”的查找,那么只需要把该单词放在两个单词边界之间,就像我们前面给出的‹\bcat\b›。第一个‹\b›要求‹c›出现在字符串的最开始处,或者是在一个非单词字符后面。
这样做的好处是,你可以放心地搜索‹omega\Z›,而不必担心在你的目标文本的最后是不是会存在一个多余的换行符。当逐行读入一个文件的时候,有些工具会包含行末的换行符,而有些工具则不然;‹omega\Z›会把这种区别隐藏起来。