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的回答

扩展阅读:

Share
 

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还剩一个半迭代时,强烈提出的。这个问题似乎从项目开始时可以预期,甚至中途客户也是偶尔提及,但我们似乎没有足够重视。我更认为这是业务理解和管理客户期望的问题。我还没有更好的想法,期待跟团队一起回顾。
Share
 

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发起查询,从而更新自己的系统状态,以期与指定状态保持一致。

 

 

Share
 

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来实现。

Share
 

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.

Share
 

OO训练营第四五课

徐X语录:

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

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

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

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

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

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

没有bad smell就不要重构。

Share
 

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,徐叉教导我们,要用代码证明自己的观点,要反过来关注问题本身。

Share