自信,从来都是说时有,用时无的。很少有人在开始讲演的时候就信心满满。大多数的情况下,讲演者都是从听众的反馈中不断积累信心的。这些反馈包括了卖关子时看见的期待眼神,听众赞同的点头,适时的提问等等。 有几个简单的实践可以帮讲演者源源不断的从观众中汲取信心。
* 合理的站姿,身体的姿态会在潜意识里影响观众对于演讲的印象,一个僵硬的站姿,会让人觉得压抑,抖腿,双手交叉等都是内心觉得不安全,想逃跑的表现。轻松站姿两年前就有人教过我,可惜境界一直没到,没领悟。 教我的人是我太极拳的师傅,老师说要“中正,安舒,沿路缠绵,静运无慌,肌肤骨节处处开张”,用在讲演中也是没错。
* 声音,洪亮的声音大概是抓观众注意力最简单的方法了,任谁也没法忽略台上站着的大喇叭。
* 适当的停顿,头至尾一直以相同的速度来进行,听众会被催眠的,譬如说我当年的政治老师就是此中高手。
* 目光接触,没有目光接触的观众会觉得被冷落,自然没兴致听下去。你也甭想得到任何正面的反馈。 如果台下都是dark look,就找几个脾气好的人目光接触吧。
* 诚实,其实是给自己减压的法门,讲演的时出错很正常,承认了,然后用备用方案就好了,没人会在意。越想掩盖,越多人会注意到。到时候大家就完全抱着看你出糗的态度了。当然如果你没准备备用方案,就不是诚实不诚实的事儿了,而是个大傻瓜。
2008年11月12日星期三
不同的思考方法
前几天产品发布,项目经理“Jez.谦虚” 同志要求我们审查一下2.0版本中有没有添加不能用于商业目的类库。因为类库很多,并且有很多的更新,删除,添加操作,很难一下找出到底在新版本中添加了那些文件。
于是我和哈达写了这样一个shell:
同时克里斯也在解决这个问题, 他把代码首先更新到上一个发布版本,然后利用meld将两个版本的ivy文件比较,人肉分析添加了哪些文件。 这个方案很简单,速度也很快。克里斯关注点在快速解决问题上,而不是“自动化的解决问题”
同组的李教授看到我们这么热闹,也饶有兴趣参与进来,他最近一直在研读mercurial宝典,对各种tip烂熟于心,他看到我们的解决方案后,写了另一段shell
这段shell充分利用了mercurial在template中定义的关键字做到了简单而强大,它可以打印出所有从上一个发布版本到最新版本间添加的文件以及相应的版本号。
解决这个问题的过程让我觉得很有兴趣, 同一个问题,由于解决者所关注的点以及知识域的不同出现了千差万别的解决方案。这些方案间的成本和收益也有着巨大的差别。
在日常工作中,我们都希望“简单而强大”的解决方案,单凭自身不断的宽展知识面是不够的,因为我们面临的问题多种多样,个人精力有限,个人的兴趣也限制了对于某些问题的深入研究,敏捷方法中的结对编程是一个很好地解决方案,更多拥有不同知识领域的人可以来共同解决一个问题,增加了我们找到“简单而强大”解决方案的几率,而开放空间可以让更多的人有机会参与到讨论中,贡献他们的聪明才智。爱凑热闹的李教授就是一例
于是我和哈达写了这样一个shell:
哈达和我都很欣赏使用shell来解决问题的方式, 所以写这段代码的时候,先想到了利用管道,以及grep过滤出所有在上次发布后和最新版本间添加的所有jar文件并把它们输出到文件中。
hg log -r 4281:tip --template '{node}\n' localivy |xargs -I % hg glog -p -r % | grep -A 1 '.*diff.*jar' | grep -B 1 'new file mode' > ~/Desktop/newlyAddedJars.diff
同时克里斯也在解决这个问题, 他把代码首先更新到上一个发布版本,然后利用meld将两个版本的ivy文件比较,人肉分析添加了哪些文件。 这个方案很简单,速度也很快。克里斯关注点在快速解决问题上,而不是“自动化的解决问题”
同组的李教授看到我们这么热闹,也饶有兴趣参与进来,他最近一直在研读mercurial宝典,对各种tip烂熟于心,他看到我们的解决方案后,写了另一段shell
hg log -r v1.0:tip --template '{file_adds} is added at revision {node}\n' localivy
这段shell充分利用了mercurial在template中定义的关键字做到了简单而强大,它可以打印出所有从上一个发布版本到最新版本间添加的文件以及相应的版本号。
解决这个问题的过程让我觉得很有兴趣, 同一个问题,由于解决者所关注的点以及知识域的不同出现了千差万别的解决方案。这些方案间的成本和收益也有着巨大的差别。
在日常工作中,我们都希望“简单而强大”的解决方案,单凭自身不断的宽展知识面是不够的,因为我们面临的问题多种多样,个人精力有限,个人的兴趣也限制了对于某些问题的深入研究,敏捷方法中的结对编程是一个很好地解决方案,更多拥有不同知识领域的人可以来共同解决一个问题,增加了我们找到“简单而强大”解决方案的几率,而开放空间可以让更多的人有机会参与到讨论中,贡献他们的聪明才智。爱凑热闹的李教授就是一例
2008年11月3日星期一
在Junit测试中使用 “前提”
在Cruise团队,QA经常会提交这样的bug, "某某功能在Windows上不工作 ",当然我并没有歧视Windows的意思,Windows在这句话里可以被替换为OSX, LINUX,乌龟SVN, Collable SVN,等等。在这样的团队中,面临着更多的不同平台(不仅仅是操作系统,还有不同发行版)带来的挑战。即便是Java这样号称“一次编写,随处运行”的语言,也必须常常去处理不同平台之间的些许差异。
作为一个TDDer,我们修复上述的bug的步骤是:
但是这样一个好的测试,却很可能无法提交,因为这特定平台的补丁可能会让别的测试失败。
这时候的无奈之选就是
这样,我们修复了bug,也编写了可靠的测试,除了测试代码有一点ugly. 这样的代码多了 ,也终究是一件恼火的事情。
前几天,写了一个对Junit的扩展,对于上面的测试,可以这样来写
在这里,引入了前提条件@Prerequisite, 只有当前提满足的时候才会运行测试。
在项目里一个真实的例子是我们使用了ab(apache出品的性能测试工具,可以通过命令行调用)来进行性能测试,但并非所有的平台都能方便的安装ab。并且这个测试也无需在所有的平台上运行,通过使用前提,可以写下测试:
在这里AppsInstalledChecker通过运行“ab -V”,并利用返回值判断是否当前平台上安装了ab,从而相应的运行或者忽略测试。
@Prerequisite中的Checker是一个接口
@Prerequisite不是用于解决产品代码中的平台问题,作为junit的扩展,可以使用它让测试代码更干净,可读。
如果有兴趣,可以访问 http://code.google.com/p/junit-ext/来尝试一下。
Feedback或者Feature Request请发到:
iamkaihu@gmail.com
zee.ho.81@gmail.com
作为一个TDDer,我们修复上述的bug的步骤是:
* 找到一台Windows机器
* 对上述Bug编写测试
* 运行测试得到“Red bar”
* 修改产品代码
* 运行测试得到"Green bar"
但是这样一个好的测试,却很可能无法提交,因为这特定平台的补丁可能会让别的测试失败。
这时候的无奈之选就是
@Test
public void featureShouldWorkOnWindows {
if (OSUtil.isWindows()) {
//Run the test.
}
}
这样,我们修复了bug,也编写了可靠的测试,除了测试代码有一点ugly. 这样的代码多了 ,也终究是一件恼火的事情。
前几天,写了一个对Junit的扩展,对于上面的测试,可以这样来写
@RunWith(PrerequisiteAwareClassRunner.class)
public class TestCasesOnDifferentOS {
@Test
@Prerequisite(checker = OSChecker.class, arguments = OSChecker.LINUX)
public void shouldPassOnLinuxPlatform() throws Exception {
}
}
在这里,引入了前提条件@Prerequisite, 只有当前提满足的时候才会运行测试。
在项目里一个真实的例子是我们使用了ab(apache出品的性能测试工具,可以通过命令行调用)来进行性能测试,但并非所有的平台都能方便的安装ab。并且这个测试也无需在所有的平台上运行,通过使用前提,可以写下测试:
@RunWith(PrerequisiteAwareClassRunner.class)
public class TestCasesOnDifferentOS {
@Test
@Prerequisite(checker = AppsInstalledChecker.class, arguments = "ab -V")
public void shouldRunPerfWhenABIsInstalled() throws Exception {
}
}
在这里AppsInstalledChecker通过运行“ab -V”,并利用返回值判断是否当前平台上安装了ab,从而相应的运行或者忽略测试。
@Prerequisite中的Checker是一个接口
package com.googlecode.junit.ext;任何人都可以通过继承这个接口在扩展出适合应用场景的Checker。
public interface Checker {
boolean satisfy();
}
@Prerequisite不是用于解决产品代码中的平台问题,作为junit的扩展,可以使用它让测试代码更干净,可读。
如果有兴趣,可以访问 http://code.google.com/p/junit-ext/来尝试一下。
Feedback或者Feature Request请发到:
iamkaihu@gmail.com
zee.ho.81@gmail.com
2008年10月20日星期一
带上用户的那顶帽子
你可曾在复杂的功能测试代码中挣扎着找出测试意图: 人肉过滤准备数据的过程. 抽丝剥茧的找到测试所覆盖的业务流程,猜测它究竟在测什么?
我们来看看这个测试:
无可否认,测试达到了目的,验证了系统行为与期待一致。问题是这个测试中的“期待”到底是什么?
在带上用户的帽子回答这个问题的时候,我们很快意识到
而后两句:
于是测试变成了:
测试变得更加可读,测试意图也变得更加明显。
作为Developer的特质之一就是细节驱动,我们将各种业务需求整理为更为详细的技术实现,并深陷其中, 造成了即便在完成功能测试的时候也不由自主的使用了经过“翻译”的语言。
特质之二就是记性不好(也许只是我),每天早上的Standup我都得靠着自己的Pair或者备忘录才能想起昨天作了什么。那么上面的测试失败时,我没信心自己会记得
脱下Developer的帽子, 换一顶用户的帽子,来跟自己玩儿Q&A
作为技术实现的:
带上用户的帽子,就是强迫自己以业务价值为角度进行思考。并将思考形式化,最终变成一种习惯。 而这种习惯将给团队带来更加简练,易读、易于维护的测试。
--
Hu Kai
blog : http://iamhukai.blogspot.com/
我们来看看这个测试:
userA logIn
rerun "defaultStage"
verify "defaultStage" is triggered and wait for completed
verify pipeline label of "defaultStage" is "1"
verify "secondStage" is triggered and wait for completed
verify pipeline Stage" is "1"
无可否认,测试达到了目的,验证了系统行为与期待一致。问题是这个测试中的“期待”到底是什么?
在带上用户的帽子回答这个问题的时候,我们很快意识到
verify "defaultStage" is triggered and wait for completed对于用户来说意味着:
verify pipeline label of "defaultStage" is "1"
newly created "defaultStage" should be ran in the same pipeline
而后两句:
verify "secondStage" is triggered and wait for completed意味着:
verify pipeline Stage" is "1"
next stage should be triggered automatically.
于是测试变成了:
userA logIn
rerun "defaultStage"
newly created "defaultStage" should be ran in the same pipeline
next stage should be triggered automatically.
测试变得更加可读,测试意图也变得更加明显。
作为Developer的特质之一就是细节驱动,我们将各种业务需求整理为更为详细的技术实现,并深陷其中, 造成了即便在完成功能测试的时候也不由自主的使用了经过“翻译”的语言。
特质之二就是记性不好(也许只是我),每天早上的Standup我都得靠着自己的Pair或者备忘录才能想起昨天作了什么。那么上面的测试失败时,我没信心自己会记得
verify "defaultStage" is triggered and wait for completed到底意味着什么?
verify pipeline label of "defaultStage" is "1"
脱下Developer的帽子, 换一顶用户的帽子,来跟自己玩儿Q&A
"我要做什么?"
“login” 并 “rerun stage.”
"然后我必须要了解什么? 对我而言,什么是有价值的信息?"
“newly created "defaultStage" should be ran in the same pipeline”
“next stage should be triggered automatically.”
作为技术实现的:
verify "defaultStage" is triggered and wait for completed是系统中的易变部分,随着系统的演化, pipeline label的格式很可能发生变化(譬如可订制),然而业务价值(newly created "defaultStage" should be ran in the same pipeline)却非常稳定,健忘如我也能立即捡起上下文。
verify pipeline label of "defaultStage" is "1"
带上用户的帽子,就是强迫自己以业务价值为角度进行思考。并将思考形式化,最终变成一种习惯。 而这种习惯将给团队带来更加简练,易读、易于维护的测试。
--
Hu Kai
blog : http://iamhukai.blogspot.com/
2008年10月15日星期三
使用Firebug的麻烦
firebug 的好处不多罗唆了,基本上可以满足开发,调试Web页面所有的需求。修改CSS, 强大的javascript调试功能, 便捷的页面Inspect,等等。
说下Firebug的Bug吧:
* 严重的内存泄露
大概是众所周知的麻烦了, 以至于当你打开gmail,它都会提醒你,firebug会让gmail变慢, bla bla bla。
会变慢多少呢?我和Tin同学很久以前写过一个小程序来测试自己的应用, 我们把每十秒一次的AJAX调用变为每一秒一次,在20分钟的时间内浏览器的内存占用从30M内存跑到900M。我们花了2天的时间来进行各个部分的javascript调优,大概让内存下降了几十M,对于整个内存泄露,简直就是杯水车薪,直到Tin同学无意间停掉firebug,同样的条件下,内存几乎没有增加。
说下Firebug的Bug吧:
* 严重的内存泄露
大概是众所周知的麻烦了, 以至于当你打开gmail,它都会提醒你,firebug会让gmail变慢, bla bla bla。
会变慢多少呢?我和Tin同学很久以前写过一个小程序来测试自己的应用, 我们把每十秒一次的AJAX调用变为每一秒一次,在20分钟的时间内浏览器的内存占用从30M内存跑到900M。我们花了2天的时间来进行各个部分的javascript调优,大概让内存下降了几十M,对于整个内存泄露,简直就是杯水车薪,直到Tin同学无意间停掉firebug,同样的条件下,内存几乎没有增加。
* 关于缓存的假象
即便你正确的设定了Cache-Control, Expire等HTTP头, 你总会发现在被缓存的js, css文件出现在Firebug的Net视图中(被缓存的图片显示正常),并告诉你,下载这个文件花费了若干秒, 当你在无尽的Search中完全找不到浏览器拒绝进行缓存的头绪时。这里的讨论 会告诉你这是Firebug的一个Bug, WebKit似乎也有同样的问题。(今天和Tin同学用Wireshark 和 Live HTTP Headers 验证了此问题,留此存照)
2008年10月9日星期四
使用Mercurial之乐
* 方便的安装。
不论是mac, linux还是windows,不论你是命令行的爱好者还是乌龟的忠实粉丝,你总能找到一款适合你的。
* 2个命令创建一个Mercuria仓库,
> hg init
> hg serve,
通过这两个命令你就可以拥有一个通过HTTP协议访问的mercurial仓库, 你可以方便的通过客户端通过命令访问,或者你可以轻松的使用浏览器来浏览当前的代码。
* 方便的分布式功能
上一次在印度我想在一台新电脑上安装源代码,无奈网络速度太慢,于是乎,我找到一个存有源码的机器,hg serve,这样我得到了一个本地服务器,通过它,我在1分钟内拿到了代码,然后将hgrc(一个mercurial的配置文件)的URL指向在中国的服务器,继续更新后面的几个patch。 将一个1个小时的操作变成2分钟的操作。
如果你急需要某个patch, 但是你的同事还没来得及提交到服务器上去,没关系,你大可以将自己的workingcopy指向同事的电脑, 运行hg pull就可以从他那里及时的拿到最新的代码。
没有branch的痛苦, 没有branch是因为每个人都是一个branch -_-!!!
* 便捷的本地提交
使用Mercurial,你可以在没有网络的情况下通过
> hg ci
进行本地提交,再也无需因为没有网络时候患上“写代码没有SCM恐惧症”,你也可以通过这个命令在日常开发中即达到小步前进,又不用每10分钟非得跑一遍测试。
* 离线操作
不论是Mercurial的提交或者是diff,rollback,strip, merge都可以在没有网络的情况下进行,想像一下在中国开发,服务器在美国的痛苦:那缓慢爬行的小乌龟。
* 速度优势
Mercurial是增量存储,并且它会每隔一段时间进行对整个Repository打一个快照,这样当你去clone repository(相当于svn checkout)的时候,它可以找到最近的一个快照,并在它的基础上应用后续的patch。
* 基于patch的管理
Mercurial将你的提交作为一个patch管理, 你可以很容易拿到别人的patch,通过hg客户端或者linux上的 patch命令将别人最新的修正打在你的工作目录里面。
* 更多的便捷操作
你想将本地的某些提交取消? hg strip
你想将server上的某些changeset取消?hg backout
你想订制hg log的输出方式?定义自己的hg template。
尝试Mercurial
不论是mac, linux还是windows,不论你是命令行的爱好者还是乌龟的忠实粉丝,你总能找到一款适合你的。
* 2个命令创建一个Mercuria仓库,
> hg init
> hg serve,
通过这两个命令你就可以拥有一个通过HTTP协议访问的mercurial仓库, 你可以方便的通过客户端通过命令访问,或者你可以轻松的使用浏览器来浏览当前的代码。
* 方便的分布式功能
上一次在印度我想在一台新电脑上安装源代码,无奈网络速度太慢,于是乎,我找到一个存有源码的机器,hg serve,这样我得到了一个本地服务器,通过它,我在1分钟内拿到了代码,然后将hgrc(一个mercurial的配置文件)的URL指向在中国的服务器,继续更新后面的几个patch。 将一个1个小时的操作变成2分钟的操作。
如果你急需要某个patch, 但是你的同事还没来得及提交到服务器上去,没关系,你大可以将自己的workingcopy指向同事的电脑, 运行hg pull就可以从他那里及时的拿到最新的代码。
没有branch的痛苦, 没有branch是因为每个人都是一个branch -_-!!!
* 便捷的本地提交
使用Mercurial,你可以在没有网络的情况下通过
> hg ci
进行本地提交,再也无需因为没有网络时候患上“写代码没有SCM恐惧症”,你也可以通过这个命令在日常开发中即达到小步前进,又不用每10分钟非得跑一遍测试。
* 离线操作
不论是Mercurial的提交或者是diff,rollback,strip, merge都可以在没有网络的情况下进行,想像一下在中国开发,服务器在美国的痛苦:那缓慢爬行的小乌龟。
* 速度优势
Mercurial是增量存储,并且它会每隔一段时间进行对整个Repository打一个快照,这样当你去clone repository(相当于svn checkout)的时候,它可以找到最近的一个快照,并在它的基础上应用后续的patch。
* 基于patch的管理
Mercurial将你的提交作为一个patch管理, 你可以很容易拿到别人的patch,通过hg客户端或者linux上的 patch命令将别人最新的修正打在你的工作目录里面。
* 更多的便捷操作
你想将本地的某些提交取消? hg strip
你想将server上的某些changeset取消?hg backout
你想订制hg log的输出方式?定义自己的hg template。
尝试Mercurial
2008年9月26日星期五
在Mac上安装Mercurial
Mercurial 是一种分布式版本工具, 它的本地提交, patch功能,以及对branch的管理非常优雅.
在ubuntu安装mercurial只需要运行sudo apt-get install mercurial
在Mac上安装Mercurial不是1-Click install
* 下载 http://mercurial.berkwood.com/
* 双击安装.
在ubuntu安装mercurial只需要运行sudo apt-get install mercurial
在Mac上安装Mercurial不是1-Click install
* 下载 http://mercurial.berkwood.com/
* 双击安装.
cat > ~/.profile* 打开hg view
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-s
hg clone http://selenic.com/hg hg-upstream
hg pull
hg update
sudo cp hg-upstream/contrib/hgk /usr/local/bin
cat > ~/.hgrc输入在 hg-upstream中运行 hg view, 应该可以看到hg view的UI
[extensions]
hgk=
[hgk]
path=/usr/bin/hgk
订阅:
博文 (Atom)