今天我和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...