分类目录归档:技术

停止使用重量级测试工具

专业的咨询师不仅需要对新兴的技术和趋势有足够的好奇心去探索,也要能够对过气的技术工具有自己的评价,这样当客户咨询师为什么做这样的技术抉择,而必须放弃手头已经的技术投资时,才能有足够的说服力。

这次要介绍的是2013年12月版ThoughtWorks Tech Radar中的Heavyweight Test Tool条目,状态是Hold。Hold就是建议停止使用。

继续阅读停止使用重量级测试工具

ORM + 遗留数据模型 = 伤不起

最近在项目中遭遇的问题,是ORM + 遗留数据模型的问题。

一个有十年以上历史的遗留系统,包括它的数据库。要在它的上游,搭建一套Web API,供更多三方系统调用和集成。为了给不同的三方系统提供统一的数据交换接口,特地建立起一套完备(至少目前是)的数据模型,并在数据库中对应设表建模。

这样一个势在必行的工作就是,在需要保持遗留系统继续无障碍工作的前提下,要实现从新数据模型到遗留数据模型之间的数据转换。客户要求使用的是NHibernate来做ORM。

问题就在于当需要将新数据模型和遗留数据模型相互关联时,ORM显得力不从心。新数据模型,独立的实体,清晰的关系,满足ORM对于数据表及关系的定义要求,换句话说新数据模型就是为ORM量身定做。而遗留数据模型,最早建立的时间还是ORM乃至Hibernate方兴未艾之际,历经时间和人手的洗礼,数据模型和表定义混乱糅杂,关系不清晰或者表达错误。

一个突出的例子就是,明明是多对一关联,却凭白增加第三张关联表,变成多对多关联。按照NHibernate的关联定义需求,映射到对象层,会出现一个列表类型的字段中只会存有一个关联对象的奇观,用脚趾头也可以想象出这是如何地说不通。

最近一周投入在这上面的时间着实太多,感触是NHibernate或者是ORM到底是在引导良好的建模实践,还是在无法有效表示遗留数据模型及关系时,纵容了不合业务的对象关系呢?

好吧,按照老马的说法,还是要给ORM更多的宽容和同情心,毕竟它给我们解决了80%我们不愿意面对的重复代码问题。不要寄希望于ORM太高,更不要苛求它。即使使用ORM,也要对它所操作的数据库层面的数据变化,有自己的深入的理解。

Add Sonar Support for .NET Project in Jenkins

My project is a .NET project, being built on running Jenkins. I would like to use Sonar to analyze this project to improve code quality.

Here are the steps to setup Sonar for .NET project:

  1. Download Sonar zip file, unzip and run bin/StartSonar.bat (on Windows 2008), then Sonar could be accessed from: http://localhost:9000
  2. Download and install(copy to Sonar plugins folder) .NET plugins for Sonar, http://docs.codehaus.org/display/SONAR/C-Sharp+Plugins+Ecosystem
  3. Install bunch tools for code quality: Gallio, FXCop, PartCover, Source Monitor, NDeps, according to http://choudhury.com/blog/2011/06/12/using-sonar-for-your-net-project/
  4. Download sonar runner, for future use.
  5. Download maven for future use.
  6. For 64-bit OS, use forFlags.exe to patch PartCover.exe and Gallio.Echo.exe, which are 32-bit, according to http://choudhury.com/blog/2011/06/12/using-sonar-for-your-net-project/
  7. Compile .NET solution/project under Debug but not Trunk, which are required by NDeps.
  8. Take following either way to analyze project:
    1. add sonar-project.properties under same folder with sln file, and add key ‘sonar.fxcop.installDirectory‘ with value of real FXCop installation in case console throws error. Do same thing for NDeps, as ‘sonar.ndeps.installDirectory
    2. add pom.xml, according to http://svn.codehaus.org/sonar-plugins/trunk/dotnet/tools/dotnet-tools-commons/src/test/resources/solution/Example/, and run ‘mvn clean install -Psonar-dotnet sonar:sonar
  9. Go to http://localhost:9000 to check Sonar report.

Add Sonar plugin in Jekins job – TBD

为什么TDD?

1. 反映真实需求

这里存在先写测试和后写测试的区别。

先说后写测试。根据很多经验,在直接写产品实现代码时,需要考虑需求,同时需要兼顾实现的细节,用什么算法和语法。在对需求和考虑和实现细节间来回,很容易让人产生对其中一方的疏忽,遗漏掉一些需求方面,甚至在实现上存在缺陷。

有人会说我可以通过后写测试来保证。第一经验是,很多人都不会在实现完成后,补充测试,因为还有更多的工作和需求需要实现。第二是开发人员很容易在后补的测试中,只是试图去测试他已有的实现,而不是需求本身,很容易遗漏掉一些边界检查之类,在测试时,已有的实现细节会在脑子里面先入为主,即使实现存在问题或有漏洞,也很难在后补的测试中测出来。我阅读过很多面试者的测试代码,很明显都是后补的,因为一些很明显的问题没有测出来,甚至已经实现的逻辑也是只测试了一部分。而这些面试者都承认。

结果就是,一旦代码签入,开始集成之后,暴露出问题,后补的测试起不到对于实现代码的保障需求的作用,开发者不能不借助调试工具,单步跟踪实现代码的每一行去寻找问题发生的原因。

再说先写测试。按照TDD的流程,先写失败的测试,再写恰好让测试成功的实现代码,最后重构,如此往复。这样的好处在于,每先写一个测试,都是在试图用自己对问题和需求的理解,来定义实现代码的架子。从测试的不同角度,范围、边界、大小、功能等等,来定义实现代码将来会是个什么样子。一个测试接着一个测试,利用TDD的过程,把实现代码恰好不多不少,正好驱动出解决这个需求和问题的实现代码来。

这里的重点在于,先写测试可以让开发者把重点放在理解需求和实现需求上,而不是一开始就陷入实现的细节中同时兼顾需求,掉入两者都兼顾不好的境地。先写的测试代码,作为副产品,可以作为验证需求的得力保障。

2. 设计在其中

先写测试对于设计的好处在于,先写测试先定义新的类,以及定义类与类之间的关系,就是在定义类与类之间如何交互,每个类如何暴露自己的接口,类和类之间的引用关系。这时,测试代码会逼迫开发者认真考虑如何分解类与类之间的耦合关系,这样产生的实现代码更容易利用了IoC和DIP的模式,实现面向接口编程。

这样实现代码的好处还在于,代码的可测试性很高,在加入更多的测试代码和新类的时候,同样借力于已有类的面向接口和依赖反转所带来的可测试性,达到新实现代码的面向接口和可测试性,这样进入良性循环。而这对于整体的代码和设计,获益良多。换句话说,测试即设计。

回头看后写测试的情况,因为从一开始开发者把重心放在实现的细节和功能需求的往复上,对于代码设计、类的关系和定义很容易疏于考虑,产生的结果可能是耦合紧,可测试性差。

3. 增强信心

在我看来,软件开发周期、软件交付最大的问题在于交付后的运行和维护阶段,正是这个阶段才是软件在持续交付价值的时期。在软件的可维护性,在这个阶段凸显价值。在软件交付后,包括软件开发周期期间,不可避免的就是开发者在根据新的需求,逐渐添加新的功能代码,或者修复一些已知的缺陷。

很多经验表明,在开发者按照需求添加一些新的代码进入系统,或者试图修复已有缺陷时,很容易导致既有功能出错,也就是新引入的代码打破了既有代码的逻辑,导致回归问题的出现。因为软件系统的可测试性差,无法做到快速频繁自动的回归测试,带来的可维护性自然也很差。

而作为TDD的副产品之一——可以快速频繁自动运行的测试代码,可以在开发者新引入代码之际,给予开发者足够的信心,每次添加一点新代码,一个方法,一个类,都已频繁运行已有相关的测试代码,来确保新引入代码不会打破已有的功能。

在持续集成中,这些测试代码可以帮助验证每次签入的代码都不会破坏掉已有的功能。这也是《重构》里面反复提到的在每个重构小步骤后都要运行所有的测试代码的原因所在。

4. 粒度和进度

按照TDD的原则,先写测试,可以让开发者在同一时间只关注在功能需求的一小部分,把功能需求且分到一定小的粒度,用测试代码去表示这样的需求,用实现代码让测试通过,实现这样的需求,然后重构。

这样的好处在于,开发者自己的注意力和重心不用在整个功能需求内的小需求点之间犹豫,每次注重解决单个小问题,解决完一个进入下一个小问题的解决。在保证小粒度实现的同时,保证进度可以随时被打断,但同时被打断时已经做完的是完整可以运行,至少是实现了部分需求的实现代码。

而后写测试的代价是,首先实现代码很有可能包含设计上的问题,甚至含有缺陷,在完美的测试代码完成之前(事实上这是不可能的),可以交付的是可能存在严重缺陷,甚至是曲解了功能需求的实现代码。

前端代码的阻抗失配

Impedance Mismatch(阻抗失配)经常被用来比喻服务器端面向对象代码和关系型数据库存储模型之间的关系,先有ORM技术,后有NoSQL(Document based,Key-value store),都是解决这个阻抗失配的方案。

最近在做的一个项目,具有丰富的前端UI,简单的后台逻辑。应客户的要求,采用ExtJS4作为前端技术,利用它极具交互性的UI和号称优雅的MVC框架,来构造应用的所有页面。

不落俗套,在应用最初搭建阶段,是和ExtJS相处的蜜月期,“优雅”的MVC框架和清晰的封装让人眼前一亮。但是随着项目的进展,在功能需求没有显著增加的同时,客户对于前端交互友好性的需求却与日俱增,不可避免地让前端构造UI及交互的代码日益复杂。

出于面向对象和封装的考虑,对于页面上大大小小的控件,我们基本上都做了封装,而且封装还是嵌套的,在大控件内部还封装着小空间。每一个控件基本上都是满足ExtJS的MVC模式,即对应至少View、Controller两个“类”。

同样出于对架构腐化的担心和对它的不断思考和优化,我们在不断地调整不同类的封装,对事件和行为做一一调整,力争做到合适的封装、减少重复代码、易于理解,从而达到更好的可维护性。

这样最直接的后果就是,在每次我们需要深入代码去解决一些问题时,要费上好一番功夫,从大控件找到内部的小控件,从一个事件发送者搜索到事件的响应者,才能确定出问题的代码可能藏在某个犄角旮旯。

对于封装如此良好、层次如此清晰的前端“面向对象”代码,我的反应开始是:这是否就是解决前端实现或者说架构最优的选择呢?可惜的是,我在Stackoverflow和Delicious上没有找到相似的讨论,倒是发现了很多对于JavaScript MVC的讨论。

以下是我自己的一点想法:

前端代码用ExtJS或者Dojo这样的rich library构建,用程序员惯常的面向对象思维和成熟的MVC架构来实现,无可厚非。但实现的过程与前端页面的渲染并无简单直接的对应关系。程序员在用熟悉的建模和框架时,不会简单地推演出到正在实现的控件及其行为在页面渲染后将以怎样的结果出现。这里需要框架自身的技术支撑和程序员对框架的理解,框架加载脚本的顺序、对MVC实现的风格,不一而足。

浏览器是从页面的开头到结束完成页面的渲染,中间包括脚本的同步或异步的加载,包括框架对于富UI控件的渲染。在页面初始渲染成功后,由框架接手完成未来发生的所有的UI交互,直至页面重新刷新。

如果把浏览器页面的渲染结果,或浏览器内存中的DOM看作是结构、样式和数据的存储,类似于关系型数据中逐行的数据,这与前端实现所采用的面向对象和MVC相对应,同样存在模式的不匹配,即阻抗失配。

不同的是,程序员不用太关心数据库如何存储数据,但不得不清楚不同浏览器如何渲染页面,及框架如何完成帮助后续的交互。

Team的一次Code Kata

上周四的中午饭后,我们team坐下来完成了一次Code Kata练习。跟之前的几次邀请PwC team同事过来pair和coach不同,这次是我们组自己内部完成一次练习,题目来自Yuheng之前的一个练习:文曲星上的猜数字游戏。

题目规则很简单:

  1. 游戏开始后,系统会随机给出四个不重复的数字。由用户输入自己猜测的四个数字。
  2. 如果数字猜对而且位置也对,就是1一个A。
  3. 如果数字猜对但位置不对,就是一个B。
  4. 返回结果是如“2A1B”这样的字串。
  5. 猜错6次,游戏提示结束。重来。

开始提出的要求简单:用TDD驱动出实际代码。

大家抽签后开始pair,Yuheng还会一边计时一边催促,pair的气氛基本保持紧张有序。

在我和XuChen pair开始代码后,我们很容易写出第一个测试1,期望返回结果是”4A0B”,最简单的代码实现让测试变绿。

当我们试图写下第二个测试2,试图期望返回结果是“2A1B”时,我们发现不得不面对如何这个游戏规则的最核心算法,但可惜的是,猛然间对这个算法没有头绪。

局面一时僵住,停顿了有两分钟。

直觉和一点经验告诉我们,这时该拆解任务了。很容易想到,如果计算出几个A和几个B是很直白的拆分。

我们可以很容易写出一个测试3,测试猜测的四个数字跟事先给定的四个数字相比,有几个A类数字(循环比较),我们称之为perfect number。

也很容易写出另外一个测试4,测试猜测的四个数字跟事先给定的四个数字相比,有几个B类数字(在上个算法的基础上再循环比较),我们称之为good number。

写完针对测试3和4的实现代码,我们发现我们已经完成了最基本的算法,功能完成了。剩下的是重构代码。

从我们这次的经验看,我有一些收获和心得:

  1. 测试就是代码的设计,你看测试3和4导致的结果就是我们有了对应的两个计算方法。你打算怎么测,你的实现代码就会是怎样的。
  2. 印证了在Wikipedia中那段话,pair programming尤其适合面对一些challenging的任务(至少开始的时候是)。
  3. 分解任务是王道。我们在不同的level拆分任务,从story拆分成task,从大问题拆分成小问题,莫不如是。
  4. 囿于经验,不期望由端到端的测试能逐步甚至一下子推导出完整、完善的代码实现。而这样的练习,恰恰是我们锻造自己经验必不可少的过程。

我们在实现代码时,仍然欠缺的是:

  1. 对于测试方法名的命名,不够清晰直白。
  2. 对于算法代码的实现,简单粗糙,还需要重构。

为什么要结对编程?

以下内容摘自myThoughtWorks中对于结对编程(Pair Programming)的讨论。

结对编程与非结对编程相比那个效率会更高?

这个问题没有一个明确的答案,没有人可以很肯定的说结对编程就是会比非结对编程更有效率。效率不单单是时间,还包含效果。

如 果对于一个技术难度不高,一个人几天就可以搞定,后期不需要维护的一个小项目来说可能结对编程在效率上会低于非结对编程。但是我们twer是关注代码质量 的,面临的项目一般都是需要持续发展,而且是具有一定规模的。对于这样的项目和我们,从长远来看,结对编程会带给我们长足的发展和更高的效率。对于这点我 总结了如下几点(欢迎补充):

  1. 代码质量。结对编程与非结对编程相比,往往更加容易写出更短 的代码,做出更好的设计,产生的更少的bug。俗话说三个臭皮匠顶过一个诸葛亮,每个人的思维 都是有瓶颈的,两个人的合作通常会考虑更多的设计选项,达成更简单,更易维护的设计。研究发现结对编程bug率降低会15%到50%,这个会根据程序员的 经验以及任务的复杂度而不同,这样就会大大减少以后修bug的时间,降低维护成本。这样我们才敢一个项目只有一个QA或者没有QA呀!
  2. 互相督促。 在结对编程的过程中我们可以相互督促,比如严格去TDD减少裸奔,提高注意力避免一个人注意力不集中或者花时间上网处理个人事宜,避免偷工减料 等等,从而也可以在某种程度上保证我们的代码质量。而且人们更不愿意打断两个结对编程的人,而单独工作的人却容易被打断,这样也增加了我们专注工作的时 间。
  3. 知识共享。在结对编程我们是知识共享的,还有我们会不停的switch pair,轮流与团队中的所有其他程序员结对编程,而不是仅与某个程序员编程,使得系统的知识在整个团队中传播,减少了程序员休假或者离开团队带来的风险,从而可以保证我们项目的可持续发展。
  4. 新人成长。 这是结对编程很重要的一个作用。曾经听大大说过,他和大熊发现,在别的公司毕业刚刚一年的毕业生在项目中可以承担的还很少,但是在我们公司很多 人已经成为了项目的骨干。这很大程度上要归功于结对。当新员工进入一个项目的时候,我们往往会安排有经验的老员工去跟他们结对,这样新员工可以更快熟悉项 目情况,更快的进入状态,更快的发现自己的不足,也可以学到更多的东西,更快的成长起来为项目做出贡献。
  5. 沟通能力。结对编程需要两个人一起讨论,一起把一件事情做完,也是对我们沟通能力的一种锻炼。让我们不再是很多人眼中只会闷头写代码的木讷的程序员了。
  6. 结对可以省钱http://www.infoq.com/cn/news/2009/06/dollar-value-of-pair-programming
  7. 徐昊说结对还可以Simple Designhttp://blog.vincentx.info/2007/03/agile-101-pair-programming-simple-design/

Mock, Stub, Fake

上次郑晔给大家做游戏,徐昊问到台上的咨询师,Mock、Stub和Fake的区别是什么?

自己虽然心里有点印象,但还是下来搜了一下:

Wikipedia的解释是这样:

Some authors[1] draw a distinction between fake and mock objects. Fakes are the simpler of the two, simply implementing the same interface as the object that they represent and returning pre-arranged responses. Thus a fake object merely provides a set of method stubs.

In the book “The Art of Unit Testing”[2] mocks are described as a fake object that helps decide if a test failed or passed, by verifying if an interaction on an object occurred or not. Everything else is defined as a stub. In that book, “Fakes” are anything that is not real. Based on their usage, they are either stubs or mocks.

Mock objects in this sense do a little more: their method implementations contain assertions of their own. This means that a true mock, in this sense, will examine the context of each call— perhaps checking the order in which its methods are called, perhaps performing tests on the data passed into the method calls as arguments.

这里还有StackOverFlow的回答

扩展阅读:

SAI项目小结

其实这个我在TW的第一个项目还没结束,现在就总结还为时尚早。但我担心我会遗忘,而且既然有心思写下来,为什么要等那么一个时机呢?

  1. JS代码组织结构和单元测试。按照客户要求,我们使用SmartClient作为客户端RIA库,这个库的优点是丰富的UI控件,但逻辑和视图耦合很紧。当我们发现JS端代码日渐庞大臃肿时,耦合紧导致维护很难,而这时已经过了第一个Release。在第二个Release中,我们讨论过用MVP来解耦,引入Jasmine作单元测试,讨论尽可能将代码做M、V、C三个角度分离并有了尝试,但仍未引起足够多的重视。我们是侥幸的,因为这个项目并不复杂,不管从技术上还是业务上,所以这个方面的技术债感受不深。我们知道我们应该做的。
  2. 代码静态分析和质量控制。我们直到第二个Release的后期,才试图引入cobertura和sonar来分析我们的所有代码,结果可想而知,一些测试没有覆盖,代码的逻辑有问题,有些是多余的分支,有些是永远不会走到的逻辑。这样又方便又能让我们学习的工具,为什么没能早点采用呢?
  3. 成熟框架。因为Team半数以上是新TWer,从一开始team并未引入Hibernate和Spring等重量级框架,但在Domain Model分析上下了功夫,这样一直平静发展到Release 1结束。Release 2开始,因为团队学习的需求和客户要求架构统一,我们同时引入Spring和Hibernate,用到的多是框架最核心和基础的技术,除了遇到个别问题比如双向关联、继承和Proxy Bean,一切都还正常,业务和技术的双重相对简单性决定也用不了多复杂的框架技术。只是在临近Release 2结束时,客户要求集成双方的系统时,我们发现引入Hibernate和Spring在这时是一种风险,或者说累赘,我们不得不合并或者修改一些配置,以期避免冲突,这个过程不那么平滑。
  4. 代码合并。最后关头时间紧,我们却发现我们要merge一个超过一万两千行代码的类,这个类基本是一个Release才merge一次。过程痛苦,IDE自作主张的分析合并让我们无法相信它,我们能做的只是纯文本的手工对比,这时Beyond Compare帮了大忙,但即使这样也消耗了我们近半个下午的时间,这还只是不到十个类文件。我在想,如果能前瞻一点再果断一点,我们定不会在这个一万两千多行的类里面再添加我们全部的实现的代码,而是挪到自己的实现类里。
  5. 管理客户期望。系统集成,是客户在Release 2还剩一个半迭代时,强烈提出的。这个问题似乎从项目开始时可以预期,甚至中途客户也是偶尔提及,但我们似乎没有足够重视。我更认为这是业务理解和管理客户期望的问题。我还没有更好的想法,期待跟团队一起回顾。

DevOps之Puppet

Puppet在一款自动化系统配置管理的工具,它可以让你在很短的时间内对大量硬件和系统基本类似的系统,进行统一的系统配置管理。

说的简单点,就像开网吧,你需要对网吧的每一台机器安装操作系统,配置完全一样的软件,比如QQ和360,供网友上网,在系统和软件有损坏时,很简单的一个恢复操作就可以让机器回到刚刚安装好操作系统和软件的状态。

Puppet就是可以干这个事儿的,不同在于,Puppet是给网络管理员用的,而针对的系统多是*nix系统,因为Puppet目前对Windows支持的很少,但这不妨碍Puppet成为DevOps实现过程中的利器,另外一个类似的工具是Chef

Puppet本身基于Ruby实现,但即使没有Ruby的经验也没甚大碍。Puppet自己提出了所谓Puppet Language,是一种DSL(Domain Specific Language)语言,用直白而描述性的语言,定义系统应该具有的状态,比如一个简单的例子tmpfile.pp:

file { 'testfile':  
    path => '/tmp/testfile',
    ensure  => present,
    mode    => 0640,
    content => "I'm a test file.",    
}

在安装了Puppet了机器上运行:

puppet apply tmpfile.pp

结果就会在/tmp下新增加一个testfile,内容是This is a test file。
这个例子太简单。除了file这种类型外,Puppet提供了大量的资源类型,供对系统的状态进行描述,比如打开(如果不存在会自动下载)某项服务,自动增加一个用户。当管理的机器成百上千,这样的自动化服务达到的效果就很可观了。

puppet apply是Puppet在单机上运行和测试的工具,真实的使用情景会是,一台机器(master)专门保留所有机器配置管理的状态信息,而每台机器(agent)会在指定的时间向master发起查询,从而更新自己的系统状态,以期与指定状态保持一致。

 

 

IFrame带来的Session问题

客户原来有个Web App系统A,我们要基于A开发一个系统B,但不希望B对A依赖太重,所以B被实现为一个独立的Web App(war)。A和B部署在同一个Weblogic server上,在A中可以导航到B,两个系统看起来像是一个系统。

有个小需求是,在B中希望显示一个页面,根据参数能展示出不同的信息。这个页面在A中已经存在,所以自然而然最快的方法就是在B中创建一个iframe来指向那个A中的页面,配以不同的参数,多快好省!

问题很快出来了,QA发现那个iframe中的A页面很快就会session timeout,然后会提示用户输入登陆用户名和密码,而iframe外的B session并未过期的。

问题在于A和B实际上还是两个Web App,各自有各自的session。因为不能简单地修改A的页面,让它保持session不会timeout,所以剩下的办法只好是控制B的session timeout时间要早于A了。还有什么更优雅的方法呢?

如果给我一次重新实现的机会,我宁愿自己画一个属于B的页面,而扔掉iframe。

另外一个需求是,A和B是各自独立的Web App对于用户是透明的,用A的用户登陆进来可以直接进入B,反之则不可以。解决办法有点tricky,A的contextRoot设为/,B的contextRoot设为/XXX,这样二者的servletContext为同一个,即可实现A把自己的loginToken放到servletContext中后由B访问到,从而在请求B页面时,检查是否能取到servletContext中的loginToken,如果不能则转向到A的登陆页面,这点由Filter来实现。

this in Selenium’s get_eval() method

At Selenium Ruby site, there is a description for method get_eval(script), as following:

Gets the result of evaluating the specified JavaScript snippet. The snippet may have multiple lines, but only the result of the last line will be returned.

Note that, by default, the snippet will run in the context of the “selenium” object itself, so this will refer to the Selenium object. Use window to refer to the window of your application, e.g. window.document.getElementById(‘foo’) If you need to use a locator to refer to a single element in your application page, you can use this.browserbot.findElement("id=foo") where “id=foo” is your locator.

The special thing here is the this reference, means when invoking method get_eval() to run some javascript snippet, the this reference in snippet would refer to (redicted?) caller Selenium object but not the original belonging object.

This does differ from what we already know for window.eval() method, in which javascript snippt this still refer to the original belonging object, see below demo code:

var a = 0;
window.eval("this.a = 1"); // this refers to window object
alert(a); // a=1
alert(this.a == a) // true

Above code demos this is window object itself.

function A(){};
A.prototype = {
  f1: function(){
    return "f1";
  },
  f2: function(){
    alert(this.f1());
  }
}
var a = new A();
window.eval("a.f2()"); // equals to a.f2(), this in f2 refers to a.

Above code demos this in a.f2() method still refers to a, but not window object.

Generally, eval() is an evil thing.

OO训练营第四五课

徐X语录:

Design Pattern是针对特定问题的特定解决方案。

是问题,而不是解决方案定义了Pattern。

模式之间存在相似性,比如Adapter和Bridge模式,只是量的差别。

先让问题再想着怎么解决 over 想清楚什么问题再下手。

面向接口编程破坏了Communication和Technical之间的平衡。会带来Communication debt。

真的需要抽取借口时,需要两个条件:1. Concret Class;2. Consumer for Interface

没有bad smell就不要重构。

Pair的节奏

这篇只想说说我对Pair节奏的理解。

Pair是需要节奏的,为什么?最简单的回答是,没有节奏,两个人都会很累。我有体会,跟team另一个新TWer一起pair,两个人会不由自主地坐在屏幕前,切分任务,你写测试我写实现,一动不动坐了整个下午,除了上厕所的时间。可想而知,离开办公室的时候那个疲惫。

不是说Pair就会比一个人“裸奔”轻松,恰恰相反,Pair就会比较累,这是无数人经验证明的。但是我们仍然需要在Pair时控制一定的节奏,不仅仅是为了减轻疲劳。

最直觉的反应是,疲劳会影响对问题的考虑,测试的覆盖度,代码的质量,这在一对Pair都疲劳的状态下,也不能免俗。当疲劳把Pair能带来的那些个好处统统赶跑后,Pair还有何存在的意义?

一般来说,对Pair经验比较少的人,节奏可以由Pair经验多的人来控制,如何划分任务,粒度以及驱动测试开发的频度如何,都可以依据Pair的双方的能力和经验对比进行安排。在划分任务和思考时,可以以提问的形式,互相提问回答,推动进度。实现代码时,可以以乒乓的形式,由一方来编写测试,另一方编写实现,并跨任务轮换,让双方都感觉到Story的实现在有条不紊地前进,充满自信。双方可以约定在一个小时左右的时间,进行一次短暂的休息,切换context,以确保有持续的精力投入后续的Pair。

因为Pair是两个人的事情,当一方因为某种原因感觉到疲劳,或者被第三方事情打扰,不能集中精力在pair中时,可以主动跟对方提出中断,以换取集中的注意力在Pair上。

如果Pair对于某个问题或技术都不熟悉,并且在可遇见的时间内无法解决,应该立即寻求有经验同事的帮助,或者暂时放下留待后期解决,坚持钻牛角尖无疑是破快Pair节奏的杀手之一。就我的经验看,Pair时钻牛角尖的几率很小,在一方出现端倪时,对方会明显感觉到并主动提出来跳出这个圈子,这应该时Pair时一个小小的好处吧。

至于可能破坏Pair节奏的另外一个杀手——两人就问题或技术有争执,一般会发上在没有较多Pair经验的人身上,这会是另外一个话题,涉及到Simple Design,徐叉教导我们,要用代码证明自己的观点,要反过来关注问题本身。

Automation Training For TW QA

今天作为酱油党之一(徐X语,还有小刀和LeoShi同行)参加了徐X为TW QA开的Automation Training。

对于Ruby小白的我,跟着QA一起学了一把。

  • Ruby的包机制,应用的配置环境
  • Selenium RC的原理,包的四个变种。
  • 在07年就提到过的Page Object

自动化测试具备怎样的特质,才能最大限度的减少QA手工测试的工作量?

  1. Effective
  2. QA manageable

最后,徐X介绍了几个月前他研究出的Data Flow大法。简言之,测试不是要去测试事情本身,而是要测试事情结果的确发生。听着象废话,但那张精美手绘图却惊艳得不行。用以作为切分并归纳测试案例的依据。很受用,争取撺掇一篇文章出来。