今天我和WPC被迫派給了一個很nonsense的活,客戶給了我們兩份名單,讓我們對照我們SSO中的用戶數據做一下數據維護,如果有的用戶在SSO中沒有就加進來;要是SSO中有,看一下OA里有沒有,如果OA里沒有寫一個列表讓OA的同志們去維護數據。本來是一個很枯燥的活,好在WPC和我都是pragmatic programers,于是生活變得有樂趣多了。
解決這個問題最直接的做法,就是login到SSO平臺上,然后一個用戶一個用戶的search,search完了再用OA Admin登陸查OA帳戶。我們是pragmatic programmer嘛,這么繁瑣的活動自然寫程序搞定它。自然浮現兩個選擇:Ruby Watri,還有就是產自俺們公司的Selenium Script。
上來先用Ruby Watri,這個東西好啊,簡單啊。WPC找了一個以前寫的example, 我照著改了一個用戶的search,然后擴展:
 1 # login in as sso admin
 2 ie = Watir::IE.start(sso_login_url)
 3 ie.text_field(:name,"username").set(sso_admin_user)
 4 ie.text_field(:name,"password").set(sso_admin_pass)
 5 ie.button(:value, "登錄").click
 6 
 7 # search user
 8 ie = Watir::IE.start(sso_search_url)
 9 ie.text_field(:name, "userName).set('張三')
10 ie.button(:value, "查找").click

跑到command line run一把,ruby login.rb,然后一個古怪的事情出現了
ie.text_field(:name, "userName).set('張三')
userName輸入框highlight了一下,然后沒有字...難道是編碼問題?換了encoding重新save了一把,結果一樣。郁悶...于是我和WPC想法是...Ruby中文有問題,不管了時間緊迫,換Selenium Test,自家的東西嘛。但是Selenium的Script是HTML-based的,寫起來太麻煩。我們是pragmatic programmer嘛,這么繁瑣的活動自然寫程序搞定它。于是我來搞一個ScriptGenerator,WPC同志搞script template。一搞template WPC同志就比較興奮,大喊:velocity! velocity!哎,我機器上沒有velocity的library,于是我決定pragmatic一把,直接writer output。找了一個Selenume Script Demo,在每行前面加上aaaa,每行末尾加上bbb,然后ctrl + f,aaa->writer.write(" bbb->"); 改幾個",introduce parameter, extract method, compose method飛快地重構之,一個hard code的generator引擎誕生了。WPC還在調template,我看了一下代碼,蠻ugly的,refactory之:
 1     private static String scriptTemplate;
 2 
 3     public static void readScriptTemplate(String templateName) {
 4         BufferedReader reader = null;
 5         try {
 6             reader = new BufferedReader(new FileReader(templateName));
 7             String line = null;
 8             StringBuffer template = new StringBuffer();
 9             while ((line = reader.readLine()) != null)
10                 template.append(line).append("\n");
11             scriptTemplate = template.toString();
12         } catch (IOException e) {
13 
14         } finally {
15             if (reader != null)
16                 try {
17                     reader.close();
18                 } catch (IOException e) {
19                 }
20         }
21     }
22 
23     public static void generatedScriptForUser(String path, String name) {
24         Writer writer = null;
25         try {
26             writer = new BufferedWriter(new FileWriter(path + "/" + name
27                     + ".html"));
28             writer.write(scriptTemplate
29                     .replaceAll("\\$\\$userName\\$\\$", name));
30         } catch (IOException e) {
31             e.printStackTrace();
32         } finally {
33             if (writer != null)
34                 try {
35                     writer.close();
36                 } catch (IOException e) {
37                 }
38         }
39 
40     }

一下子少了無數代碼,爽多了。然后wpc也搞好了template,按模版文件一generating,幾十個selenium test script就出現了。嗯,write program that write program,有夠pragmatic。

寫了一個Test Suite,放到改一下Selenium Runner下跑一下又傻眼了。Selenium做的Functional Test,一般假定和被測的應用在一個URL base里,因此這樣local“測”remoting就不太好,而且我們又是一個安全平臺,URL base做安全基準的。一下就所有測試就crackdown在這里了。郁悶啊...
Selenium文檔,發現可以用driver來adpater local和remoting的環境,問題是這個drvier要自己寫...郁悶...
WPC在firefox上裝了一個Selenium Recorder的plug in可以記錄web page actions,然后replay。他發現這個東西能繞過Selenium的限制,于是決定看看他怎么實現的,找到code一看...原來是把selenium runner hack了...用javascript把url base生生的給改了...WPC說好啊,我們寫一個Firefox Selenium Recorder Plugin的plug in吧,讓他從一個目錄里自動load script...然后WPC開始玩firefox plugin.

我決得我還是看看Ruby的中文支持吧,找來找去都沒有說Ruby的中文有問題的,后來發現一個老大測了一下Ruby的中文字符串,說沒問題。我忘了這個老大的URL了找到再補上。代碼上看起來似乎也沒什么問題。于是我懷疑是Watri的問題,看Watri的代碼,發現Watri的設計思路就是為了模擬人的錄入,然后找到這樣的代碼:
def doKeyPress(value)



for i in 0 .. value.length-1   
   sleep @ieController.typingspeed   # typing speed
   c 
= value[i,1]
   #@ieController.log  
" adding c.chr " + c  #.chr.to_s
   @o.value 
= @o.value.to_s + c   #c.chr
   fire_key_events
end



end
根據設定的延時模擬人敲擊鍵盤。每一個間隔用String slice來輸入。于是我試驗了一下ruby的中文字符串切片:

1 value = "哈哈"
2 for i in 0..value.length-1 
3   puts value[i,1]
4 end
Ruby果然瞎菜了...value.length是4,每一個切片都是空...啊~~~~天啊,8bit char...C的時代啊。找到了問題就好辦了,我權衡了fix watri unicode和直接hack它,最后我選擇直接hack它,方法簡單:

 1 if @ieController.typingspeed != -1            
 2   for i in 0 .. value.length-1   
 3   sleep @ieController.typingspeed   # typing speed
 4   c = value[i,1]
 5   #@ieController.log  " adding c.chr " + c  #.chr.to_s
 6   @o.value = @o.value.to_s + c   #c.chr
 7   fire_key_events
 8   end
 9 else
10   @o.value = value
11   fire_key_events
12 end

然后測試一下:
1 require 'Watir'
2 
3 ie = Watir::IE.start("http://www.google.com")
4 ie.typingspeed = -1
5 ie.text_field(:name, "q").set("哈哈")

搞定。于是準備改Ruby腳本,這個時候客戶下班了,我們決定明天繼續,一共用時2小時...

最后說一下需求,一共有多少數據呢?70條...如果pair錄入的話,大約40-50分鐘吧

結論:

1.Pragmatic Programmer都是很懶的,重復5次的工作都回用代碼來寫。
2.Pragmatic Programmer都是很有好奇心的,太多的重復性勞動只能分散他們的注意力,不知道會搞出什么了,我估計我要沒有hack Watri,WPC已經開始寫Firefox plugin了。
3.Pragmatic Programmer都是“古程序員”,寫程序操縱計算機是很有樂趣的。
4.比一個Pragmatic Programmer更能折騰的是兩個Pragmatic Programmer...