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