• 2014-12-03

    Moco 0.10.0发布

    Tag:moco

    前版信息:Moco 0.9.2发布

    我很高兴地宣布,Moco 0.10.0发布了。

    Moco是什么?

    Moco是一个可以轻松搭建测试服务器的框架/工具/程序库。

    变更

    本次发布最大的变更是加入了Socket的支持。

    除了HTTP,Socket是另一种常见的集成方式,对Socket的支持让Moco能够更为全面地对集成进行支持。创建一个Socket的服务器,可以采用socketServer:

      final SocketServer server = socketServer(12306);

    与HTTP支持类似,Socket服务器也是需要设定请求以及对应的应答:

      server.request(by("foo")).response("bar");

    与HTTP本身支持很多参数不同,Socket只支持与内容相关的部分,比如,text、file、xml、json、match、exist、latency、template等等。更多细节,请参见Socket API的文档

    因为增加了不同的服务器类型,Moco独立服务器的启动参数也有所调整,原来的一个start已经不足以满足需要了。在最新的版本中,你可以根据服务类型记性启动,比如,启动一个Socket服务器的方式如下:

      java -jar moco-runner--standalone.jar socket -p 12306 -c foo.json 

    HTTP和HTTPS服务器的启动参数分别对应着http和https,为了兼容原有版本,start依然得到保留,但不确定未来是否会长期存在下去。

    这个版本增加了一个许多人要求的新特性,在Java代码中可以使用JSON配置文件。这种用法与Moco设计JSON API的初衷有很大差别,但在实际的使用中,确实有很多人这么用,所以,在这个版本里提供了一个更简洁的API,不过,这个API存在于Moco Runner包中:

      jsonHttpServer(12306, file("foo.json"));

    在API方面,

    • 增加attachment API,直接对下载附件提供支持。
    • 增加了template提取器的支持,比如,如下代码就会从请求中提取内容作为应答返回值。
        server.response(template("${foo}", "foo", jsonPath("$.book.price")));

    在Moco独立服务器和shell版本,增加了版本查询功能,以便对一些外部工具进行支持。

    另外,由于JSON Path底层实现的升级,可能会引入一些破坏性的变化,如果你用到JSON Path API,请注意。

    更多发布相关信息,请参考Release Notes

    感谢

    感谢Alex Soto,提供Moco服务关闭的解决方案,增强了Moco服务器的稳定性。
    感谢方志刚,提供Moco的Shell版本在Cygwin下运行的支持。

  • 2014-11-30

    读《不敢止步》

    Tag:书评

    十几年前,我刚开始工作那会儿,我如饥似渴地找各种的与软件开发相关的材料来读,包括《程序员》杂志。于是,我注意了一个叫“透明”的家伙,写东西文笔很好。后来,因为讨论一些技术问题,我们俩就联系上了,然后,就把“熊节”,“透明”,“gigix”划上了等号。再后来,我加入了ThoughtWorks,第一次见到了活的熊节。

    我依然记得那天,我在新笔记本上装了Ubuntu,我问他怎么联网,他说,你上网,我传个驱动给你。汗……

    从那天开始,我们俩已经同事了七年半的时间,在很多项目上合作过,以ThoughtWorks如今的标准看,我们俩的合作次数和时间也算是很多了。我早就知道他在写一本书,关于他自己这十几年的故事,作为一个和他认识了十几年,共事了七年半的人,我也很好奇他会怎么写我。如今书出版了,我当然会读一下。

    于我而言,这是一本岁月随想,把我带回了那些过去的日子。我很自然地将书分成了两个部分,以他加入ThoughtWorks为界。

    即便是今天,我偶尔也会调侃熊节是搞媒体的。他做媒体人那段日子,恰逢软件开发世界风云突变,有很多大家不断争论的东西。是.NET还是Java,是EJB还是Without EJB,是瀑布还是敏捷。那些年,各路大神你来我往,各种观点风起云涌,争论很大,每个人都有自己的思考,不断地拓展着理解,即便是作为看客,也很过瘾。时至今日,虽然新技术依然层出不穷,但大方向已定,那种思维之争已一去不复返了。抑或是今天的人都忙着各自创新去了,没有闲做思维之辩了。对于错过那段历史的年轻程序员,这本书可以带着回到那个唇枪舌剑的年代。

    他加入ThoughtWorks之后的事情,我都知道。即便是在我加入之前的故事,这么多年吃饭聊天下来,也都听了几遍了。但是,跟着他一起回顾ThoughtWorks这么多年走下来的历程,也颇为感慨。其实,许多人不知道,ThoughtWorks中国区在最初的几年里,有几次已经危在旦夕了。直白地说,不赚钱。在那些不怎么赚钱的日子里,其实作为基层员工,大家活得还是很开心的,拼了命地研究各种各样的技术,经常会有神秘兮兮地跑过来,展示他刚刚做好的东西,那时候,招聘的标准也很高,几乎个个身怀绝技。后来,这些家伙几乎个顶个是现在公司的顶梁柱。

    为了生存,公司调整了方向,随之而来的就是规模的扩张,这也不断地挑战着这些“老员工”的思考方式。从只写代码到带人,甚至做管理,ThoughtWorks中国区的思路不断地调整着,这些老家伙们也不断改变着自己的角色。如今,最初的程序员,留在公司的,似乎只有我还在一线每天写代码了。其他更多人已经各方面的管理者了。即便是我,也干了很多跟程序不搭边的事,比如,郑大晔校。

    熊节在2012年去了成都,开辟新办公室。作为老战友,我也在下半年跑到成都支持他,一干就是一年半。以战绩论,成都的结果真是惊人,第一年就超过了西安办公室两年的发展速度,而且这样的高速一点都没有停下来的迹象。由程序员转成管理者,熊节做得还真不赖。这些年是ThoughtWorks不断发展变化的一段时间,所以,我们这些土鳖程序员为了支撑公司发展,不断地拓展着自己的视野和思路。随着一个公司长大,这样的机会不常有,现在更多的人加入多半是在享受发展的结果。当然,机会永远是有的,只要有心。

    我还是很贪心的,2013年并没有出现在这本书里,否则,我的Moco一定会位列其中,因为2013年,我们玩出了创新的新花样。后来,熊节去了非洲,支援其他办公室建设去了。

    抛开我和熊节熟人的关系不论,我个人还是很喜欢看这种个人发展的真实故事。尤其是像熊节这样的文笔写出来,一定是比较好看的。多谢熊节用这本书带我回忆那过去的事情。勘个误吧,至少关于我,也算是补充一下那段历史:

    • 2007年,敏捷中国大会,原本是我和Ola Bini一起讲东西,结果,他没来,我只好一个人上了。
    • 2008年,Starwood项目,关于IE上出Bug的故事,其实我用的是Dell笔记本,但是,装的是Ubuntu,所以,没发现IE的问题。
    • 2011年,熊节说他叫我去西安,可是,我2010年就已经转到了西安。
    • 2011年,索勤的故事,当时,我其实在墨尔本,那个时候的熊节不太相信人是可以培养的,而我则刚好相反。现在索勤已经是公司里面独挡一面的高手了。

    这其实算不上一篇书评,只是借着这本书,回想一下过去的日子罢了。我个人还有个期待,再过十年或是十二年,我希望看到这个故事的新篇章。

  • “default method”是Java 8引入的一个特性,其初衷是为了解决既有程序库扩展的问题。在之前的Java版本中,如果要给一个已有接口添加新方法,这会带来一些问题,因为新方法没有对应的实现,所以,实现这个接口的类就会编译不通过。而“default method”的引入给了方法一个实现,编译就可以通过了,从而我们可以在不改变已有代码的前提下,为程序库增加新的方法。

    但是,既然接口方法可以有实现,那它也给了我们另一种思路。

    在Java开发中,我们可能会经常面临一种情况。以Web开发为例,假设我们有一个领域对象Foo。

    class Foo {
      ...
    }

    我们有个需求,根据其某些属性决定是否在页面上隐藏它。你当然用一个类实现它,但在页面上是否隐藏它,显然不应该属于领域对象的一部分。所以,我们通常会用另外一个类封装它,比如HiddenableFoo。

    class HiddenableFoo extends Foo {
      boolean isHidden() {
        ...
      }
    }

    好,新需求来了,我们要在页面上决定是否要给它的名字加粗,于是,这个类就成了HiddenableBoldableFoo。

    class HiddenableBoldableFoo extends HiddenableFoo {
      boolean isBold() {
        ...
      }
    }

    这里其实有个问题,为什么不是先由BoldableFoo,然后,从它继承呢?我们暂且不关心这个细节。

    又有一个需求来了,在另一个页面,我们需要确定这个对象是否需要隐藏以及是否需要斜体:

    class HiddenableItalicableFoo extends HiddenableFoo {
      boolean isItablic() {
        ...
      }
    }

    又有一个页面,需要的判断一下某些地方是否要加粗,某些地方判断要斜体,那这个类要怎么做呢?

    class BoldableItalicableFoo {
      boolean isBold() {
        ...
      }

      boolean isItablic() {
        ...
      }
    }

    如果这里的isBold和isItablic与前面的实现是一样的,是不是重复代码就此出现了呢?这就是在Java 8之前,我们面对的问题,我们可以继承接口,但实现不成。

    Java 8来了,“default method”就给了我们一个机会,让我们可以继承实现。下面是一种实现:

    interface Fooable {
      ...
    }

    class Foo implments Fooable {
      ...
    }

    interface Hiddenable extends Fooable {
      default boolean isHidden() {
        ...
      }
    }

    interface Boldable extends Fooable {
      default boolean isBold() {
        ...
      }
    }

    interface Italicable extends Fooable {
      default boolean isItablic() {
        ...
      }
    }

    这里之所以引入一个Fooable接口,因为接口只能继承接口。有了这样的基础,我们就可以自由组合了,比如:

    class HiddenableBoldableItalicableFoo extends Foo implements Hiddenable, Boldable, Italicable {
    }

    如果你熟悉Ruby,这俨然就是Mixin,对于Scala粉丝来说,Trait已然呼之欲出,C++人则会看到多重继承的影子。是的,所有这些语言特性背后都有一个共同的理念:面向组合的编程。

    Trygve Reenskaug和James Coplien在2009年提出的DCI架构,它是一种很好的面向对象编程的视角,其基础就是这种面向组合编程的理念。DCI架构在Java社区里面一直没有很好的讨论,也是因为Java语言没有给力的支持。

    诚如前面所说,Java语言之前是不支持面向组合编程的,但是,在Java社区里,有人不断地做着这方面的探索,Rickard Oberg,这个前JBoss架构师,实现了一个Qi4J的框架。不过,现在Java语言本身也可以这么做了。

    当然,相比于其它的语言,Java在这方面的表现力是有限的,因为它的基础是接口,所以:

    • 它不能有字段
    • 所有方法只能是public的

    另外,组合只能是基于类来做,就像HiddenableBoldableItalicableFoo,虽然这个类除了声明什么都没有。相比于Ruby的Mixin这种可以在运行时扩展的特性,更是表现力要弱了许多。

    但与之前的版本相比,Java算是在这个方面迈了一步,至少我们可以不用再为同一份代码多处出现而纠结了。

  • 2012年时,我写过一篇blog,谈到了加入项目时,如何了解一个项目。

    当我加入项目时,我要了解什么

    最近在客户现场做咨询,有人跑过来,问了我同样的话题。经过简单沟通,我发现他真正的问题是,为什么我们作为一个外来人可以很快地上手他们的项目,而他们自己的新员工却很长时间才能上手。

    这是一个很有趣的问题。

    正如我在前面那篇文章里写到的,通常我加入一个项目,我首先要获得这个项目的大图景。作为一个ThoughtWorker,我一定会了解这个项目业务价值,它要解决的问题是什么。即便是技术,我也会尝试先从架构入手。等有了大图景,具体到一个问题时,我就知道它在整个项目中,是拼图的哪一块。

    对比而言,客户的很多新人加入一个项目时,常常是试图从一个具体的问题着手,在解决问题的过程中,有太多小障碍了,每次遇到的几乎都是一个全新的问题。所以,几乎就是一路磕磕绊绊。当然,这里不排除我比客户的大多数新人工作经验丰富,所以,对于大部分技术理解能力要比他们好。

    我看到的另外一点大的差别是,我了解项目时,我会按照自己的思路,通过问题,一步步将项目分解开来,也就是从业务到技术,从大框架到小模块。通常,只要与我交流的人对项目足够了解,基本上,我都可以把项目大致梳理出来。

    相较而言,一些新人的接收模式是,等着别人按照他们的思路来介绍。以我和许多客户的合作经历来看,大多数经年累月的项目往往是不足够清晰,甚至是相当混乱的,在这个项目上长时间工作的人也很难把它梳理得很清楚,所以,指望他们把项目介绍清楚只能祈求好运了。前面那篇文章我提到过,许多人是把业务和技术混在一起的,这是我经历过的实际情况。

    除了按照上面所说的方式理解项目,之所以会给人一个很快上手项目的感觉,可能还有一点:使用“行话”,也就是客户他们常用的术语。与人用“行话”交流,往往会更容易建立彼此的信任,也会让人认为你对交流的东西很懂行,这或许算是附赠的技巧吧!

  • 2014-07-02

    Moco 0.9.2发布

    Tag:moco

    前版信息:Moco 0.9.1发布

    我很高兴地宣布,Moco 0.9.2发布了。

    Moco是什么?

    Moco是一个可以轻松搭建测试服务器的框架/工具/程序库。

    变更

    本次发布最大的变更是加入了HTTPS的支持。

    HTTPS服务器的创建即不同于普通的HTTP服务器,它使用的是httpsServer方法,除了类似于HTTP服务器的参数之外,一个很重要的参数是certificate,这里需要给出相应文件以及对应keystore密码和certificate密码。

    final HttpsCertificate certificate = certificate(pathResource("cert.jks"), "mocohttps", "mocohttps");
    final HttpsServer server = httpsServer(12306, certificate);

    独立服务器用户也可以通过命令行生成一个HTTPS服务器:

    java -jar moco-runner-<version>-standalone.jar start -p 12306 -c foo.json --https /path/to/cert.jks --cert mocohttps --keystore mocohttps

    还有一个比较重要的调整,在JSON配置中,增加了直接对JSON的支持,比如,

    {
       "request": {
           "uri": "/json_response_shortcut"
       },
       "response": {
           "json": {
               "foo" : "bar"
           }
       }
    }

    这样,给出应答就会是一个JSON对象:

    {
       "foo" : "bar"
    }

    而原来的做法如果需要返回一个JSON对象,需要大量的转义字符或是存放到文件中。

    在API方面,也做了许多调整:

    • 在Java API中,增加了HTTP版本协议类,无需以字符串的方式制定HTTP版本。
    • 在匹配方面,增加了更多的运算符,比如startsWith、endsWith、contain、exist。
    • 对于RequestHit的验证,增加了between运算符,可以判断请求次数在某个区间内。
    • 增加了多个Request Monitor的接口,以便处理遗留代码的时候,可以同时进行验证和查看日志。
    • 在模板接口上,将模板变量的类型由Object接口改成了String,这样,API用户必须确定好模板变量具体的表现形式。

    更多的细节请参考ReleaseNotes

    感谢

    感谢Michal Svab,实现了HTTPS API部分。