Wenbing Li

It's all about the fundamentals.

Cucumber Best Practices

| Comments

cucumber is a BDD(Behaviour driver development) framework. Basically test cases are written in plain text which is called Gherkin language, just in Given-When-Then steps. After that, developer will write implementation for those steps.

It’s good to bridge the communication between business requirement and implementation. The ideal work flow would be business analysis or QA write features and developer implements them. However, in a real world, developer write both of them. Cucumber is different programming model than what we normmaly use like Java, ruby, etc. It is not mature enough like OO which has tons of patterns and best practices to apply.

Below are some best practices when I use cucumber for one project.

Write declarative features

Scenarios should be written like a user would describe them.

Beware of scenarios that only describe clicking links and filling in form fields, or of steps that contain code or CSS selectors. This is just another variant of programming, but certainly not a feature description.

Bad

1
2
3
4
5
6
7
8
9
10
11
Scenario: Adding a todo item
    Given I have a todo list named "Mondays list"
    When I go to the home page
    And I fill in "username" with "dave"
    And I fill in "password" with "secret"
    And I press "Log In"
    And I go to the todo page
    And I click on link "Mondays list"
    And I fill in "todo" with "Grab some milk"
    And I press "Add todo"
    Then I should see "Todo item added successfully"

Good

1
2
3
4
5
Scenario: Adding a todo item
    Given I have a todo list
    And I am logged in as a normal user
    When I add a todo item
    Then It should be added to the todo list

Use As a <role>, I want <goal/desire> So that <benefit> format for feature description

Describe feature in user story style. Starts the feature and gives it a title, then follow with user story format.

Bad

1
2
3
Feature: Create an account

  Scenario: ...

Good

1
2
3
4
5
6
Feature: Create an account
  As a user
  I want to create an account for me
  So that I can re-login to the same account

  Scenario: ...

Use should in each Then and following And steps

The purpose of Then steps is to observe outcomes and verify result. Using should word as a convention makes it easy to understand, and make sure do verification in implementation step.

Bad

1
Then My job is displayed in the table

*Good

1
Then My job should be displayed in the table

Capitalize first letter of every step

Each step is independant, capitalize first letter makes a nice format.

Bad

1
Then my job should be displayed in the table

Good

1
Then My job should be displayed in the table

Use page object model

Don’t write implementation in step definition, use page object model.

Bad

1
2
3
4
5
6
7
8
When(/^I search for my job$/) do
  visit path_to('manage jobs')
  find('a', :text => 'Filter Jobs').click
  fill_in('users-id-search', :with=>"")
  page.find('#users-id-search').native.send_keys(:backspace)
  fill_in('requisition.title', :with => @job.title)
  click_button('Search')
end

Good

job_steps.rb

1
2
3
4
When(/^I search for my job$/) do
  @app.manage_job_page.load
  @app.manage_job_page.search @job.title
end

manage_job_page.rb

1
2
3
4
5
6
7
def search(job_title)
  find('a', :text => 'Filter Jobs').click
  fill_in('users-id-search', :with=>"")
  page.find('#users-id-search').native.send_keys(:backspace)
  fill_in('requisition.title', :with => @job.title)
  click_button('Search')
end

Use background

If all the scenario in one feature file have the same steps, put them in the background.

Bad

1
2
3
4
5
6
7
Scenario: Foo
  Given I am logged in as an admin
  And ....

Scenario: Bar
  Given I am logged in as an admin
  And ....

Good

1
2
3
4
5
6
7
8
Background:
  Given I am logged in as an admin

Scenario: Foo
  Given ....

Scenario: Bar
  Given ....

Use Tags

Tags are a great way to organise your features and scenarios.

Good

1
2
@job @smoke
Feature: Job management

Avoid to use step params

Step params is a smell which the feature is not declarative.

The parameter in feature should not be used as implementation parameter.

Bad

1
Given I complete registration from using email "test@example.com"

Good

1
Given I complete registration form using a valid email

Avoid to use scenario outlines

A Scenario Outline provides a parametrized scenario script. However, it easily become an anti-pattern if you use it too many.

Each scenario with example actually means a different scenario. It’s better to use scenario with a meaningful name instead of using scenario example.

Scenario outlines uses step params, which should avoid to use.

Beware of feature that only has one scenario but have a long list of examples. It could be a smell that too many different scenario are put together.

Avoid to use data tables

Data tables provides data for implementation. It is against the rule of feature should be declarative and business focus.

Feature should not provide any detail implementation or data for testing.

Use Capybara find whenever possible

find will wait for a set amount of time and continuously retry finding the element until either the element is found or the time expires.

Bad

1
first(".active").click

Good

1
find(".active", match: :first).click

Using Atom Editor in Ubuntu

| Comments

Atom is a new editor from github.com, which is very similar to Sublime. It’s written using nodejs, coffeescript and less. And it’s opensource. It makes lots of potentials for the future of the editor.

However, atom does not provide a Linux package yet. As it’s opensource, we can build from source.

Build Atom Editor for Ubuntu 14.04

From atom github repo you can find the Linux installation guide. Basically just following this guide should be OK.

The only problem I met is node-gyp rebuild issue. It’s because of the gyp installed in your machine which is conflicting with gyp version in node-gyp.

Simple solution is :

1
sudo apt-get remove gyp

After that, you will find a .deb atom file under /tmp/atom-build folder. Then install it :

1
2
cd /tmp/atom-build
sudo dpkg -i atom-0.95.0-amd64.deb

Now you can open it by command atom.

Configure keybindings for Ubuntu

Most of the keybindings in atom are for Mac OS. You can add your own keys in key map file ~/.atom/keymap.cson. There are some basic hotkeys I have added for Ubuntu:

1
2
3
4
5
6
7
'.editor':
    'ctrl-l': 'editor:select-line'
    'ctrl-d': 'find-and-replace:select-next'

'body':
  'ctrl-pagedown': 'pane:show-next-item'
  'ctrl-pageup': 'pane:show-previous-item'

Check the source code e.g. edit-view.coffee for available commands you can use.

Happy hacking!

Only Allow GET for Specified Request Path Using Mod Security

| Comments

Mod Security

Recently we want to directly use couchdb REST API. But only expose GET REST API so the data could be safe for the public.

A simple mod security rule do this trick:

SecRule REQUEST_URI "/database" "chain,log,deny,status:403,phase:2,id:1234567010"
SecRule REQUEST_METHOD "!@rx ^(?:GET)$" 

Two layers application - AngularJS + CouchDB

Speaking to this mod security, we are actually working some simple two layers application, only front-end(AngularJS) and datastore(Couchdb).

This simple stack suits for some applications, which only load data from backend.And backend data would be updated by application operator.Those applications could be FAQ or News website etc. We use CouchDB because it has very good REST API and Web UI to manage the data.

So no application layer, :-)

Start Blogging

| Comments

Blogging, NOW!

以前有一个blog,但是各种原因最后连之前的文章都找不回来了。这也是一个很好的教训,能不自己hosting就不要自己hosting,数据备份还是不如source code管理容易,于是我就选择了当然颇为流行的github+octopress.

到新西兰快两年的时间,做个各种各样的事情,其实还是不错的,接触面还是挺广的。基本上每天都在解决各种各样的问题,每每解决了问题,心里都有一种强烈的冲动把它blog下来,谈不上说分享,更多想记录一下自己做的事情,希望能两年后知道自己之前做了什么事情。一直觉得家里忙(本人是两个孩子的爸了哦,:)),但这一切都不是借口,要做就现在做吧。

于是买了域名(wbinglee.com),就开干了。至于为什么要用这个域名,只是它和我太有缘了吧。我的gmail的帐号就是这个,很多网站的帐号也是这个,那域名也就这个吧,看起来无比的熟悉。

新的blog中关于技术问题,我会尽量使用英文(虽然不咋地…);其他杂七杂八的就中文吧,英文肯定写不出感觉。

好,开始了。

用jconsole来管理WebSphere AppServer的MBean

| Comments

最近需要在WebSphere中做点魔术,所以要使用WAS(WebSphere Application Server)中的管理能力。JMX是WAS的管理的核心,苦于WAS Mbean文档并不是那么详细,这个时候就可以使用jconsole了。jconsole是一个JDK自带的JMX兼容的JVM管理工具,可以用它来可视化的查看和操作Mbean。

在启动jconsole时需要一些WAS的jar,所以我这样来做:

  • 创建一个文件夹,例如:C:\programs\jconsole
  • 将一些需要的jar 拷贝进入到这个文件夹的libs中:
1
2
3
4
com.ibm.ws.admin.client_6.1.0.jar   (在<WAS_HOME>/runtimes)   
ibmorbapi.jar  (在<WAS_HOME>/java/jre/lib)
ibmorb.jar (在<WAS_HOME>/java/jre/lib)
ibmcfw.jar (在<WAS_HOME>/java/jre/lib)
  • 编写一个bat文件
1
2
3
4
5
6
7
8
9
10
11
12
set JAVA_HOME="C:\Program Files\Java\jdk1.6.0_16"
set WAS6.1_JARS=C:\programs\jconsole\libs

set BOOTJARS=%WAS6.1_JARS%\ibmorbapi.jar
set BOOTJARS=%BOOTJARS%;%WAS6.1_JARS%\ibmorb.jar
set BOOTJARS=%BOOTJARS%;%WAS6.1_JARS%\ibmcfw.jar

set CLASSPATH=%WAS6.1_JARS%\com.ibm.ws.admin.client_6.1.0.jar
set CLASSPATH=%CLASSPATH%;%JAVA_HOME%\lib\tools.jar
set CLASSPATH=%CLASSPATH%;%JAVA_HOME%\lib\jconsole.jar

%JAVA_HOME%\bin\jconsole  -J-Xbootclasspath/p:%BOOTJARS% -J-Djava.class.path=%CLASSPATH%
  • 使用如下URL来远程连接WAS中的JMX server
1
service:jmx:iiop://localhost:2809/jndi/JMXConnector

其中这个端口号是RMI Connector的端口号,可以在启动的logs发现。

1
ADMC0026I:The RMI Connector is available at port  2809

或者到admin console中查看 Application servers > server1 > Administration Services > JMX connectors

好,这样就可以了。

一直在写

| Comments

刚和同学聊天时,问我blog怎么好长时间都没有更新了。看了看上篇文章的日期,离现在也有两个月有余了,有些惭愧。最近在做一些有意思的技术调研,倒是会在接下来的文章中分享一些。其实这段日子也在写一些东西,不过不在自己的blog上面了罢了。

时间总是匆匆,让我们继续前行…

读《PPK on JavaScript》

| Comments

PPK也是在JavaScript世界中的风云人物了,这位老兄对于浏览器端的技术以及各种浏览器的兼容性有极其丰富的经验。在这本书中,他谈了很多关于可访问性(Accessibility)和可用性(Usability)的一些问题,非常有趣。比如他说“不同的开发者以不同的方式诠释了JavaScript的目的。简单而形象地说就是:深受CSS革命影响的传统Web开发者们,创建的是瘦的、可访问性很强、乱糟糟的JavaScript代码;而来至服务器端开发的‘资深程序员们’用完美的面向对象代码、创建的是胖的、可访问性很差的Ajax客户端”。显然,ppk同学应该属于写乱糟糟但可访问性很好的人,而现在我做的大量事情却是后者。他更多相信现在的Ajax只是一种泡沫,当这个泡沫破灭并且大量‘资深服务器程序员’消失时,JS的开发者会更加注重可访问性。 可能体会不到ppk经历浏览器各种痛苦的经历,但是总体来说浏览器都在坚定地遵循Web标准,JavaScript的支持也会成为浏览器必备要求。anyway,事情总在发展,好戏在后头。分享一下我觉得这本书中几个有趣的地方。

可访问性和可用性

可访问性(Accessibility)是指你的网页对于任何人、在任何环境下都是可持续访问的。特别是指某些用户,比如弱视、浏览器不支持JavaScript或者另外一些情况,比如用户使用Mobile使用你的网页等等。而可用性(Usability)是指使用或者浏览你的网页的容易程度(这里我们只谈web页面),通常它指我们能更有效率地使用、更容易地学习以及更加满意地使用它。举个例子来说,你让你的web页面支持IE6,或者支持mobile都是在提高它的可访问性;而是用CSS来改善布局让用户更容易阅读、使用JavaScript做一些对用户有帮助的互动都是在提高它的可用性。

Web页面都是由下面三个层组成的,通过它们我们可以了解到它们之间的关系以及它们和可访问性和可用性之间的关系。

  • HTML结构层

  • CSS表现层

  • JavaScript行为层

web3layers

Web页面的三个层,HTML结构层是必需的基础,CSS表现层和JavaScript行为层建于它之上。所以在客户端代码中不得不关注的话题就是这三个层的关注点分离。具体探讨一下这三个的分离:

表现与结构的分离(CSS与HTML)

这个分离很好理解,基本思想就是确保HTML来定义结构,而所有的表现都定义在另外单独的CSS文件中。HTML不应该出现<font>标签和用于表现的表格。如果想定义字体和布局,都应该在CSS中处理。

大部分情况下面我们知道达到某个效果是修改表现或者结构是清楚的。但是有些情况下当更改HTML和修改CSS都可以时,你需要慎重地思考到底哪种是合理的。比如一个节点,你希望它不显示,那么你可以在HTML上面删除该节点或者使用CSS来“display:none”。当这种情况是,需要自己分析所需要的效果属于哪种情况,然后修改合理的层。

行为与结构的分离(JavaScript与HTML)

这个也比较好理解,就是不要把任何的JavaScript代码写到你的HTML页面中。应该把所有JavaScript代码放到一个独立的js文件中,然后将它链入到所有需要它的HTML页面中。

关于这个有一个有趣的话题就是:无侵入脚本编程(unobtrusive scripting).简单来说它就是通过HTML和JavaScript的分离以达到页面的可访问性和可用性的最大化。既JavaScript失效了,页面还是可阅读和理解的;而通过引入脚本和JavaScript的hook,就可以让脚本运行,增强可用性。

行为与表现的分离(JavaScript与CSS)

这个的分离是非常复杂的,而且并没有总结出什么特别系统的规则。CSS和JavaScript是有重合的灰色地带的,有时候完全不能确切地把某个效果归为表现还是行为。比如是使用CSS中的hover还是JavaScript的mouseover/mouseout。基本来说你自己得根据具体情况做合理的选择吧。

事件捕捉模型

在HTML的事件模型中有一个有趣的话题。这个简单的问题就是:如果一个节点和它的父亲节点都有对同一个事件的处理,那么到底哪个事件会被先执行呢?这就是关于事件的冒泡和捕获。事件冒泡是说事件从它的目标元素开始,沿着文档树依次向上冒泡,并触发相应的事件处理函数。而事件的捕捉是刚好相反的,它从文档的第一级开始,然后沿着文档树向下游,知道事件目标为止。

在W3C模型中,捕获和冒泡都会发生。当一个事件触发时,它先被文档捕获,到了事件目标后,再冒泡到文档顶层。而传统模型和微软模型只支持事件冒泡,而不支持事件捕获。所以最好是限制使用事件冒泡。其实在我们的实际编程中,很少关心这个话题,是因为大部分情况我们都只使用了事件冒泡。

更加具体的可以查看ppk的文章:http://www.quirksmode.org/js/events_order.html

小记

应该来说PPK所谈的JavaScript是一个更加全面的浏览器编程的世界,让人可以全面来了解这个世界包含的东西。其实大部分的内容在PPK的网站上都有,值得读读。http://www.quirksmode.org/js/contents.html

蓝色的血液

| Comments

我是一名pure blue,从在IBM实习到开始工作到现在,在这样的一家百年老店工作,虽然有太多让人抱怨的地方,但是你会喜欢这里的人(虽然不是全部),并且从心里慢慢接受它的文化和价值观。这样的一个公司的价值观是令人敬佩的,足以指导你做人做事。特别不喜欢一些人张口闭口说这不如人那不爽,只怕自己没有自信罢了。不足就努力去改变它,你真受不了了就quit,what’s the big deal?一个人在外人面前骂娘(自己老娘)的时候,是不可能得到尊重的,只是哗众取宠罢了。当你流淌着蓝色的血液时,你是自豪的。

今早一聊天之不爽,故有此文。下面摘除一些从《品味蓝色》上句子。我很喜欢。

任何一名优秀的IBM人,当你问他成功之道的时候,他也许无法教会你具体的流程和方法,但他一定会告诉你,没有什么,不过是秉承诚信负责之道好好做人、好好做事罢了,因为在IBM,机会永远青睐于那些有好的工作表现和正直诚实的人。 – 周伟焜

学会尊重别人,同时赢得别人尊重,这很重要。尊重别人身上的优点,你会变得更宽容。执着、专业、深入是赢得他人尊重的根本。谁都有缺点,包括我们的公司。但是要学会用宽容的心来对待周围的一切。得到尊重,你每天都会快乐。 – 郭希文

所谓分享成功其实就是看你怎样Increase your influence,这并不是仅指依靠手中的权力发号施令,而是指能够提供给别人有价值的意见或见地。同时,在团队合作中,“诚信(integrity)”则是最重要的原则。无论对公司,还是对在公司团队中工作的个体,诚信都是立足之本,信任之基。对一个科学家来说,诚信就更重要,科学是不容半点虚假成分的,谁想投机取巧,违背科学规律,只能是搬起石头砸自己的脚。– 叶天正

认真积极地工作,让我在事业上可以不断进步;以尊重、信任的态度与家人、同事沟通,让我得到了家人的支持,同事的尊敬,从而拥有了和谐美满的家庭和成功的事业。面对这一切,我最深的感受就是四个字——“知福惜福”。 –于雪莉

sMash Stakeholder Meeting

| Comments

上周进行了sMash的stakeholder meeting,在这个会上我介绍了Flow的新的特性,并且进行Demo的演示。我们的演示大概从5分钟开始,之前是产品开发经理的一些介绍。本人英文口语比较烂,不知道他们到底听懂了没有。详细的信息可以看projectzero 的blog。Vido在Viddler上,至于为什么在viddler上,我想可能是为了照顾中国用户无法访问YouTube的原因吧,呵呵。

http://www.viddler.com/explore/projectzero/videos/4/

Dojo Widget的析构过程

| Comments

了解dojo widget(或者说dijit)的析构过程,不仅让你更加了解整个dijit的生命周期,同样也能帮助我们在自己定制化的dijit中如何正确地释放资源。(这里讨论的dojo应该是在0.9或者以上版本的)

下面是dijit的析构过程:

                        destroyRecursive
                    /                      \
                destroy                   destroyDescendants
        /        |        \
uninitialize  disconnect() destroyRendering

一些常见的错误是如下:

  • 使用destroy()去销毁一个dijit。我们应该使用destroyRecursive()去销毁一个dijit,从上面的过程可以看出,destroyRecursive()会销毁其孩子widgets。

  • 使用destory()去销毁定制dijit中的资源。更可怕的是有的代码可能是直接覆盖destroy,而根本不调用_Widget中的destory。uninitialize()才是dijit暴露出来给定制化widget进行析构的stub function。

结论

使用destroyRecursive()去销毁dijit,使用uninitialize()在定制化的dijit来释放自己的资源。destroyDescendants,destroyRendering基本上用不到,也不要去覆盖它们。