努力工作,把握幸福。

发布新日志

  • curl 用法(转)

    2014-05-04 15:01:40

    http://blog.51yip.com/linux/1049.html
  • CentOS YUM installation

    2014-02-17 17:04:02

    1. Uninstall the original yum

    # rpm -aq | grep yum|xargs rpm -e --nodeps

    2. Download the centos yum installation file

    3. Install Centos yum

    # rpm -ivh python-iniparse-0.3.1-2.1.el6.noarch.rpm
    # rpm -ivh yum-metadata-parser-1.1.2-16.el6.x86_64.rpm
    # rpm -ivh yum-3.2.29-40.el6.centos.noarch.rpm yum-plugin-fastestmirror-1.1.30-14.el6.noarch.rpm

    4. Update the yum source, we use the 163 centos image source

    # cd /etc/yum.repos.d/
    # wget  http://mirrors.163.com/.help/CentOS6-Base-163.repo
    # vi CentOS6-Base-163.repo

    edit CentOS6-Base-163.repo ,replace the $releasever to version number,6 and save!or copy the following content to CentOS6-Base-163.repo

    #########################################################################
    # CentOS-Base.repo
    #
    # The mirror system uses the connecting IP address of the client and the
    # update status of each mirror to pick mirrors that are updated to and
    # geographically close to the client.  You should use this for CentOS updates
    # unless you are manually picking other mirrors.
    #
    # If the mirrorlist= does not work for you, as a fall back you can try the
    # remarked out baseurl= line instead.
    #
    #
    
    [base]
    name=CentOS-6 - Base - 163.com
    baseurl=http://mirrors.163.com/centos/6/os/$basearch/
    #mirrorlist=http://mirrorlist.centos.org/?release=6&arch=$basearch&repo=os
    gpgcheck=1
    gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
    
    #released updates
    [updates]
    name=CentOS-6 - Updates - 163.com
    baseurl=http://mirrors.163.com/centos/6/updates/$basearch/
    #mirrorlist=http://mirrorlist.centos.org/?release=6&arch=$basearch&repo=updates
    gpgcheck=1
    gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
    
    #additional packages that may be useful
    [extras]
    name=CentOS-6 - Extras - 163.com
    baseurl=http://mirrors.163.com/centos/6/extras/$basearch/
    #mirrorlist=http://mirrorlist.centos.org/?release=6&arch=$basearch&repo=extras
    gpgcheck=1
    gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
    
    #additional packages that extend functionality of existing packages
    [centosplus]
    name=CentOS-6 - Plus - 163.com
    baseurl=http://mirrors.163.com/centos/6/centosplus/$basearch/
    #mirrorlist=http://mirrorlist.centos.org/?release=6&arch=$basearch&repo=centosplus
    gpgcheck=1
    enabled=0
    gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
    
    #contrib - packages by Centos Users
    [contrib]
    name=CentOS-6 - Contrib - 163.com
    baseurl=http://mirrors.163.com/centos/6/contrib/$basearch/
    #mirrorlist=http://mirrorlist.centos.org/?release=6&arch=$basearch&repo=contrib
    gpgcheck=1
    enabled=0
    gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
    
    #########################################################################

    5. clear yum cache

    # yum clean all
    # yum makecache     # cache the server information to local, improve the installation speed.

    6. install tool with yum -- require your Linux server can access Internet

    # yum install xxx, such as

    # yum install ftp

    # yum install telnet

    # yum install tmpwatch

    once you have installed this tool, when running this command, it would tell you 'nothing to do'

    7. install tool with yum -- once your Linux server has no access to Internet

    download the related rpm from Internet

    then upload the rpm to your Linux server, run command

    rpm -ivh xxx.rpm --nodeps --force

    --nodeps //don't check dependency library

    --force //install it forcibly

  • Java 远程调用Shell

    2014-01-21 13:29:22

    Remote Shell Scripts need to export the ENV variable again.

    public class JavaRemoteRunShell {
        

        private Connection conn;
        private String ipAddr;
        private String charset = Charset.defaultCharset().toString();
        private String userName;
        private String password;

        public JavaRemoteRunShell(String ipAddr, String userName, String password, String charset) {
           this.ipAddr = ipAddr;
           this.userName = userName;
           this.password = password;
           if(charset != null) {
            this.charset = charset;
           }
        }

        public boolean login() throws IOException {
           conn = new Connection(ipAddr);
           conn.connect(); //
           return conn.authenticateWithPassword(userName, password); //
        }



        public String exec(String cmds) {
           InputStream in = null;
           String result = "";
           try {
            if (this.login()) {
             Session session = conn.openSession(); // open one session
             session.execCommand(cmds);
             in = session.getStdout();
             result = this.processStdout(in, this.charset);
             System.out.println(result);

             System.out.println("ExitCode: " + session.getExitStatus());
             session.close();
             conn.close();
            }
           } catch (IOException e1) {
            e1.printStackTrace();
           }
           return result;
        }

       
        public String processStdout(InputStream in, String charset) {
           byte[] buf = new byte[1024];
           StringBuffer sb = new StringBuffer();
           try {
            while (in.read(buf) != -1) {
             sb.append(new String(buf, charset));
            }
           } catch (IOException e) {
            e.printStackTrace();
           }
           return sb.toString();
        }
       
        public static void lsViaSSH(String hostname, String username, String password) throws Exception {
           

           
                Connection conn = new Connection(hostname);

                conn.connect();

                boolean isAuthenticated = conn.authenticateWithPassword(username, password);
                System.out.println(isAuthenticated);

               if (isAuthenticated = true){

                Session sess = conn.openSession();

                sess.execCommand("sh test.sh " );

                InputStream stdout = new StreamGobbler(sess.getStdout());

                BufferedReader br = new BufferedReader(new InputStreamReader(stdout));

             

                sess.close();
                conn.close();
           
               }

       
        }
       
        public static void main(String[] args ) throws Exception{
           
            String ipAddr = "127.0.0.1";
            String userName = "test";
            String password = "password";
             String charset = Charset.defaultCharset().toString();
            JavaRemoteRunShell c = new JavaRemoteRunShell( ipAddr,  userName,  password,  charset);
       
            c.login();
            String cmd1="sh test.sh";
           
            System.out.println(c.exec(cmd1));
           
        }
        }
  • Spring bean 的创建

    2012-07-24 11:03:34

    http://graykeel.iteye.com/blog/696984
  • Unix Shell 学习(三)-- () 与 {}

    2012-06-14 17:20:54

    1.{} 的用法
    确认你有一个叫file和一个叫file1的变量。能够使用以下的语句给它们赋值:
    $ file=this
    $ file1=that
    $echo $fileand$file1 寻找变量fileand,file1
    sh: fileand: parameter not set
    $ echo ${file} and $file1 寻找变量file,file1
    thisandthat
    花括号被用来区分变量名和周围的文本
    2.()的用法
    命令替代
    语法:
    $(command)
    例子:
    $pwd
    /home/user2
    $ curdir=$(pwd)
    $ echo $curdir
    /home/user2
    $ cd /tmp
    $ pwd
    $ cd $curdir
    $ pwd
    /home/user2
    命令替代用来替代一个命令和命令行输出。命令替代的标准语法,也是POSIX鼓励的一种语法是:$(command).
    命令替代让你捕获一个命令的输出,用它作为另一个命令的参数,或是赋值给一个变量。象在变量替代中一样,命令替代的执行是在命令行开始之前完成的。当命令行输出包含回车换行,它们会被空格代替。
    同变量替代相似,命令替代使用一个美元符号之后的用括号包围的一个命令。
    所有有效的shell脚本都可以加入命令替代。Shell 扫描每行脚本,执行它发现的开始于一个开括号,结束与于一个闭括号的命令。
    命令替代的另外一种格式是用反引号来环绕一个命令象:
    `command`
    它和$(command) 是等价的,并且这是Bourne Shell认证的唯一的形式。`command`形式可以用在POSIX的脚本中和Bourne Shell的脚本中。
    命令替代通常是在将一个命令的输出赋给一个变量或以后的处理时使用。通常pwd命令将它的输出送到你的屏幕。当你执行以下的赋值语句:
    $ curdir=$(pwd) 或 $ curdir=`pwd`
    pwd 的输出被赋给变量 curdir。
  • Unix Shell 学习(二)--循环结构语法

    2012-06-14 15:26:27

    循环分为 for 循环, while 循环 和 until 循环

    For 循环

    语法:  for 变量 in 列表
           do
           操作
           done
          
            注: 变量指循环内部用来指代当前所指代那个列表中的那个对象
            列表指for 循环内部要操作的对象,可以是字符串也可以是文件,文件必须是文件名
      
        例:删除垃圾箱中所有的.gz文件

          #delete all of .gz file from dustbin

          for i in $HOME/dustbin/*.gz
          do
          rm?Cf $i
          echo "$i had been deleted"
          done

    While 循环

    语法: While 表达式
          do
          操作
          done
    只要表达式成立, do 和done之间的操作就会一直进行

    Until 循环

    语法: until表达式
          do
          操作
          done
    重复 do和done之间的操作,直到表达式成立

    例:
         #test until
         # add 1 from 100
         total=0
         num=0
         until test num?Ceq100
         total=`expr $total +$num`
         num = `expr $num +1`
         done
         echo "The result is $total"


  • Shell 学习 --- 如何查看某端口被那个程序占用

    2012-06-03 12:09:23

    netstat -an|grep 8080(非root可用)
  • Unix Shell 学习 (一)--变量表达式

    2012-05-29 18:22:48


    1. 字符串比较

    作用: 测试字符串是否相等,长度是否为0, 字符串是否为NULL(注:bash 区分零长度字符串和空字符串)

    常用字符串操作符:

    = 比较两个字符串是否相同, 同则是为“是”,
    != 比较两个字符串是否相同,不同则为“是”,
    -n 比较字符串长度是否大于0, 大于0 则为“是”
    -z 比较字符串长度是否等于0, 等于0则为“是”

    2. 数字比较
    -eq 等于
    -ge 大于等于
    -le 小于等于
    -ne 不等于
    -gt 大于
    -lt 小于

    3. 逻辑操作
    -a or (and) 两个逻辑值都为是,才返回 真
    -o or (or)  其中一个逻辑值为是,就返回真

    4. 文件操作

    -d 对象存在且为目录,返回值为“是”
    -f 对象存在且为文件,返回值为“是”
    -L 对象存在且为符号连接返回值为“是”
    -r 对象存在且为可读则返回值为“是”
    -s 对象存在且长度为非零返回值为“是”
    -w 对象存在且科协则返回值是“是”
    -x 对象存在且可执行则返回值为“是”


  • 安装 TDS

    2011-11-14 14:08:15

    1.概念
    IBM的Tivoli Directory Server是IBM对 Lightweight
    Directory Access Protocol (LDAP)的实现,它可以跨平台的进行安装配置。TDS提供了一个使用DB2数据库对目录信息进行存储的服务器,一个将LDAP操作路由到其他服务器上的代理服务器,一个客户端,一个管理服务器的图形界面,一个管理用户的图形界面。

    2.在Linux上的安装
    1)首先确认机器上的 DB2版本是否与欲装的TDS版本匹配,你也可以使用db2_install程序安装TDS自带的DB2版本,在安装结束要求输入Keyword时,输入 ESE,安装完毕后你可以检查一下/tmp/db2_install_log.99999看看是否安装成功,99999是安装产生的随机数。
    2)要安装proxy server或者full directory server
    以root身份登录,安装如下两个包(32bit机器):
    rpm -ihv idsldap-cltbase62-6.2.0-0.i386.rpm
    rpm -ihv idsldap-clt32bit62-6.2.0-0.i386.rpm
    若想安装proxy server,则安装以下四个包:
    rpm -ihv idsldap-cltjava62-6.2.0-0.i386.rpm
    rpm -ihv idsldap-srvbase32bit62-6.2.0-0.i386.rpm
    rpm -ihv idsldap-srvproxy32bit62-6.2.0-0.i386.rpm
    rpm -ihv idsldap-ent62-6.2.0-0.i386.rpm
    若想安装full directory server,则安装以下四个包:
    3)确认安装是否正确:
    rpm -qa | grep idsldap
    4)安装语言包:
    英语:rpm -ihv idsldap-msg62-en-6.2.0-0.noarch.rpm
    中文:rpm -ihv idsldap-msg62-zh_CN-6.2.0-0.noarch.rpm
    5)安装client:
    rpm -ihv idsldap-cltbase62-6.2.0-0.i386.rpm
    rpm -ihv idsldap-clt32bit62-6.2.0-0.i386.rpm
    6)安装Web Administration Tool
    rpm -ihv idsldap-webadmin62-6.2.0-0.i386.rpm

    2.创建和管理实例:
    使用Instance Administration Tool,这个工具可以以图形界面的方式对目录服务器进行配置,Linux下它的路径是
    /opt/ibm/ldap/V6.2/sbin/idsxinst
    当然也可以使用命令行

    1)建立默认实例:
    首先要了解默认实例的属性,这些是你不能更改的:(6.1与此不同)

    Name: dsrdbm01
    Instance location: /home/dsrdbm01. (On Solaris systems, this directory is /export/home/dsrdbm01.)
    Group name: dbsysadm
    Administrator DN: cn=root
    Database name: dsrdbm01

    1>/opt/ibm/ldap/V6.2/sbin/idsxinst,打开管理工具。
    2>创建->创建默认实例Create default instance
    3>User password 中输入密码,然后确认,Encryption seed处填入一组字符串来产生AES加密键值。字符串符合下列规则:
    ASCII characters with values in the range of 33 to 126, and must be a minimum of 12 and a maximum of 1016 characters in length.
    4>输入Administrator DN密码,对于默认实例,管理DN就是root,在这你要指定一个密码。
    5>Finish

    2)创建一个实例(你自己指定设置)
    0.首先创建一个用户,然后将其加入到idsldap组内。
    adduser xxx
    usermod -a -G root idsldap
    1>/opt/ibm/ldap/V6.2/sbin/idsxinst,打开管理工具。
    2>创建->Create a new directory server instance(要是你想让这个目录服务器实例作为代理服务器实例则勾选Set up as proxy)
    3>User name中你若是想使用系统中的用户,则选择,否则就创建user(先在home下建立一个目录,然后创建一个组)。
    注意千万不要使用任何用户名中带有大写字母的用户。
    在primary中加入root
    4>Instance location一般选择就在Home下你指定的用户目录中
    5>Encryption seed string填写一个字符串
    6>Use encryption salt value你若是想迁移并且希望目录服务器实例和你迁移的实例加密同步,或者与其他目录服务器实例加密同步,则勾选。Encryption salt string类比于Encryption seed string。
    7>实例描述可填可不填
    8>TCP/IP port可以使用默认
    9>Administrator DN也可以选择默认
    10>选择已有的数据库,在Database name填入1-8个字符。
    11>选择字符集
    12>Finished

    命令行下创建实例:
    创建实例myinst,端口389,安全端口636, 加密种子mysecretkey!, encryption salt为mysecretsalt, DB2实例为myinst。
    0.首先创建一个用户,然后将其加入到idsldap组内。
    adduser xxx
    usermod -a -G root idsldap
    1.idsicrt -I testtds –p 389 –s 636 –e mysecretkey! -g mysecretsalt -t dbinst
    其中database是数据库实例,The instance name must be an existing user ID on the machine and must be no greater than 8 characters in length.
    2.idsdnpw -I testtds -p 12345678
    密码为12345678
    3.idscfgdb -I testtds –a dbinst –w 12345678 –t TDS –l /home/dbinst
    配置相连接的数据库。

    3)开启或停止目录服务器或目录服务器实例
    对于实例:
    idsslapd -I instancename
    idsslapd -I instancename -k
    对于整个服务器:
    idsdiradm -I instancename
    idsdiradm -I instancename -k(只能进行本地操作)
    ibmdirctl -D -w -h
    -p admstop
    (可以进行本地和远端操作)


    4)修改TCP/IP设置:
    主要使用idssethost这个命令,具体参照IBM Tivoli Directory Server Version 6.2 Command Reference

    5)查看TDS实例的信息:
    主要使用idsilist这个命令,具体参照IBM Tivoli Directory Server Version 6.2 Command Reference

    6)删除一个TDS实例:
    idsidrop -I instancename
    若是想将其所关联的DB2数据库实例也销毁则用:
    idsidrop -I instancename -r

    7)取消一个TDS实例与一个DB2实例的关联idsucfgdb -n -I myinstance

    8)图形化配置实例
    IBM Tivoli Directory Server Configuration Tool
    启动命令idsxcfg -I instancename

    9)修改数据库拥有者的密码
    idscfgdb -I testtds -w newpasswrd

    10)备份数据:
    对于a full directory server:使用idsdbback
    idsdbback -I testtds -k /backupdir
    对于代理服务器,使用migbkup,这个命令是不备份数据的。
    migbkup /home/idsinst/idsldap-testtds/ /home/tdsbkup
    这是因为代理服务器没有数据库相连接,不需要也无法备份数据。

    对于a full directory server:
    idsdbrestore -I ldapdb2 -k /backupdir
    对于代理服务器:
    idsdbrestore –I –k –x -n

    11)打开、关闭更改日志:
    idscfgchglg命令

    12)设置自动加载:
    在inittab文件中加入:
    srv1:2345:on
    ce:/opt/ibm/ldap/V6.2/sbin/ibmslapd -I server_name > /dev/null 2>&1
    格式是id:runlevels:action:process
    其中ID是1-4位的UID,runlevel是系统启动级别,action是动作,process是相关进程。

    13.修改主要管理员的DN和密码:
    idsdnpw -u -p

    14.监测服务器状态
    idsldapsearch -h -p -b cn=monitor -s base bjectclass=*

    15.查看系统容量信息
    一个Root DSE入口包含了一个LDAP服务器实例的是新,这些信息通过一次root DSE搜索可以查询到。
    列出root DSE的属性和值,OIDS支持和使能的容量,OIDS支持的扩展和控制方式:
    idsldapsearch -s base -b "" bjectclass=*
    列出系统现在支持容量:
    idsldapsearch -s base -b "" bjectclass=* ibm-supportedcapabilities
    列出系统现在可用容量:
    idsldapsearch -s base -b "" bjectclass=* ibm-enabledcapabilities

    16.检查服务器连接状态:
    查看连接状态:
    idsldapsearch -D -w -h -p
    -b cn=connections,cn=monitor -s base bjectclass=*
    结束一个连接事件:
    指定DN:idsldapexop -D -w -op unbind -dn cn=john
    指定IP:idsldapexop -D -w -op unbind -ip 9.182.173.43
    在指定的IP上结束一个指定DN:
    idsldapexop -D -w -op unbind -dn cn=john -ip 9.182.173.43
    结束所有连接:
    idsldapexop -D -w -op unbind -all

    17.管理连接性质
    这个功能使管理员通过关闭客户端连接以防止锁死服务器,它还可以使管理员在后端繁忙处理任务的时候还总是可以登录服务器。
    idsldapmodify -b -r -i /tmp/entrymods
    其中的filename是一个文件,一般建立在tmp下边,不过随便的。其中包含的内容举例如下:
    dn: cn=Modify Me, o=University of Higher Learning, c=US
    changetype: modify
    replace: mail
    mail: modme@student.of.life.edu
    -
    add: title
    title: Grand Poobah
    -
    add: jpegPhoto
    jpegPhoto: /tmp/modme.jpeg
    -
    delete: description
    -

    这个配置文件会Modify Me取得mail的属性改为:modme@student.of.life.edu
    并且加一个标题:Grand Poobah
    加一项jpegPhoto,内容为 /tmp/modme.jpeg
    彻底删除description
  • 【转】敏捷开发:拥抱变化的产品开发流程

    2011-08-01 15:51:39

    本文作者: 陈序明(chenxm21@gmail.com)
    摘要:随着Agile 敏捷开发流程的流行,越来越多的公司采用敏捷开发流程用于软件产品和应用的开发。
    作者的产品开发团队近两年前开始采用敏捷开发方法,一直实践到现在,并取得了不错的成果,比如:产品功
    能更加符合市场和业务人员的需求,开发效率也有了提高。文章从实践的角度介绍作者所在团队的产品敏捷开
    发过程,和作者的敏捷开发的体会。
    1. 产品中进行敏捷开发的体会
    敏捷开发近两年来,对在产品开发中应用敏捷开发有了很深刻的体会。首先说下产品背景,我参与的产品
    是面向行业的产品,在全世界都有客户,有10 年历史,和一百多个基于不同版本的客户,我们的团队完全负
    责产品的未来发展方向、发布计划、架构、设计、开发进度、测试、客户支持等。在这样一个面向全球的产品
    和自主的团队环境中进行敏捷开发体会尤其深刻。
    1) 敏捷开发中,注重概念和架构设计,而轻详细设计
    这里的概念设计,可以看成是为什么要做这个产品或模块,强调的是产品的路线规划,市场趋势,客户价
    值,技术的趋势等。架构设计,可以看成从整体上看,概念设计应该用什么方式实现,分几个层次,多少组件,
    不同层次和组件之间关系是什么。详细设计,是具体的设计和做法,API 接口等。
    一个产品,特别是面向行业的产品,概念设计和架构设计非常重要,需要考虑行业未来的发展方向,产品
    在市场中横向和纵向的比较,技术的发展方向,和每个模块的投入和收益的比例等,这样才能尽可能保证产品
    沿着正确的方向前进。在产品中新增或删除一个模块需要非常谨慎,因为一旦新增模块被客户使用,以后就很
    难在产品中去掉这个模块,因为你需要考虑产品各个版本之间的兼容性,以及客户的升级迁移。所以在开始正
    式开发之前,通过概念设计和架构设计,梳理思路是非常必要的。
    2) SWOT 分析
    以前在做项目时,大多是从技术的角度来考虑哪一些功能模块需要做,哪一些功能模块先做,而没有一个
    系统化的分析方法。造成的结果是有一些功能模块投入了很多资源,但却并不一定是客户最想要的。
    在敏捷开发中,更加注重客户需求,如果对产品进行SWOT 分析,就能选出能够付出最小的工作量,但
    能获得最大价值的模块。
    SWOT 分析阶段会在概念设计和架构设计之后进行,输入是概念设计和架构设计,输出是模块的重要度,
    和需要的时间。这样按照性价比可以进行排序,选出最能符合市场的模块。
    一款产品哪个模块重要,哪个先做,需要花多少资源和时间投入,花这么多时间和资源的模块是否在客户
    心中有相应的重要程度等等,这些都是由这款产品的市场策略来决定。所有产品都是为了市场和赢利为目的,
    在Agile 中,更好的帮助企业实现了这一点。
    3) 业务和客户驱动,而非技术驱动
    这点说是体会,也可以说是教训。在我们产品开发过程中,在某一新版本中重新设计了老版本的某一个重
    要模块,而引发了几个问题:一是,新版本的模块和老版本模块的兼容性问题,导致老版本客户无法平滑的迁
    移到新版本。二是,新版本的改进是纯技术方面的重新实现,不管对客户而言,还是对内部的架构而言,都没
    有明显好处。最后导致的结果是我们花了很多资源和人力去重新实现,但是在最后由于种种考虑还是废弃了重
    新实现的模块,依然沿用老模块。
    在产品的敏捷开发中,虽说拥抱变化,但不以为着盲目的变化,产品的改动需要经过概念设计、架构设计
    以及SWOT 分析后,三思而后行。敏捷开发中也强调“在整个项目开发期间,业务人员和开发人员必须天天
    都在一起工作”,确保技术人员能够开发出客户需要的产品。
    4) 时刻考虑版本兼容性
    敏捷开发,废除了过多冗余的文档和繁杂的设计,强调拥抱变化。但作为产品,敏捷开发不意味着盲目的
    去变化。
    当设计变动、API 接口重构、配置文件变更时,要时刻考虑产品的架构和规划路线图,和老版本的兼容性
    及迁移平滑性。否则,随着版本的增多,必将面对着大量的维护工作。
    5) 轻文档,但非无文档
    敏捷开发强调沟通的重要性,而轻冗余文档。但敏捷开发并不意味着无文档。在敏捷开发过程中,适量的
    文档还是很有帮助。有助于整理思路,加快沟通和讨论。
    我们产品中的文档包括:概念设计文档,架构图,当前版本要实现的功能列表,以及SWOT 分析。
    这些文档在每个产品版本开始之前会有产生,在每个迭代的过程中根据业务人员和市场的反馈也会有一些变
    更。通过我们实践证明,这对产品的思路,沟通讨论都非常有帮助。
    而且这些文档,大多是几页PPT,书写和维护工作都很小。
    2. 产品敏捷开发过程
    敏捷开发改进了产品的开发流程,提高了整个团队的效率。下面分析敏捷开发前和敏捷开发后的产品开发
    的各个阶段。
    1) 敏捷开发“前“的产品开发过程

    图1 敏捷前开发流程
    上图是敏捷开发前我们产品一个版本的开发流程,整个开发大概持续一年左右。从图中可以看出,流程中
    的大多数活动都是串行进行。这样的一种类似瀑布的开发流程,前提是需求在产品的初始阶段就完整的被捕获
    并正确的分析,这样才能保证最后交付的产品是客户所需要的产品,但通常这样的理想状况很难实现。
    类瀑布的开发流程缺乏灵活性,无法通过开发活动来发现不够确切的需求,导致产品无法随着业务人员和
    市场的反馈而随需应变,开发出符合业务人员需求的产品。
    2) 敏捷开发“后“的产品开发过程

    图2 敏捷后开发流程
    上图是敏捷开发“后”我们产品一个版本的开发流程,整个开发大概也是持续一年左右。但每个迭代都是
    1 个月时间。和敏捷开发前相比,有很多的区别和优点,下面是其中几点:
    ?? 市场和需求驱动,拥抱变化。
    在我们产品敏捷开发中,每个迭代结束,都会有一个产品迭代演示大会,把这个月的开发结果演
    示给组员,业务人员,售前,甚至客户,并收集反馈,并。此外,在开发的过程中,产品的业务人员
    和售前时刻保持和产品开发团队的沟通和工作。保证开发出来的产品是符合业务需求。
    ?? 充分利用资源和时间
    敏捷开发前,产品的需求设计阶段占用了整个开发流程的35%左右的时间,这段时间只需要几个核心
    的架构师和设计人员,无法充分的利用开发和测试人员的时间。敏捷开发后,跌待开发,强调沟通,缩减
    文档,在每个迭代的初期就可以充分的利用开发、测试人员的时间,达到效率最大化。
    ?? 每日交付
    产品开发过程中,每天都会做自动化Build,并生成可以交付的产品。业务人员,客户都可以试用并
    提供反馈和新需求。
    ?? 充分自动化
    敏捷开发强调拥抱变化,这必然带来动荡的产品代码变更,每一个新的功能和修改的功能,都可以影
    响到其他功能,造成副作用,所以需要自动化去支持变化,在变化的同时保证质量和开发速度。如:编译
    自动化,单元测试自动化,功能测试自动化,UI 测试自动化、集成测试自动化等。
    3. 产品敏捷开发中架构师和Scrum Master的重要性
    流程的变化必将带来岗位和职责的变化,下面是在产品敏捷开发中两个重要的人物角色:
    1) 产品架构师
    在产品的敏捷开发中,特别是我所参与的产品是面向行业的产品,架构师是个举足轻重的角色,需要有深
    厚的行业背景,创新能力,以及架构能力。
    产品是为了解决一类客户需求而纯在。但是客户的需求往往是会随着业务的发展而变化,而且竞争对手也
    会有类似产品的推出。所以一个产品推出市场后,所具有的功能模块慢慢的会越来越成熟,并且拥有越来越多
    的竞争对手,并且慢慢的失去竞争力。一个好的产品,特别是面向行业的产品,要具有长期的生命力,需要具
    有下图所示的产品模型:

    图3 产品发展模型
    ?? 成熟的模块:指的是推出市场有一段时间,这些功能模块满足了客户的需求而被广泛使用,而且市场
    趋于稳定,大量竞争对手的产品也推出了类似的功能。这些成熟模块,都是产品的基本模块,不代表
    产品的竞争力。产品中如果只具有这一些功能模块,那随着需求和竞争的激烈,慢慢会走向灭亡。如
    90 年代的BP 呼机一样,当手机一旦推出,这个产品也就走向灭亡。
    ?? 发展中的模块:指的是刚推出市场并且具有强劲的市场生命力,符合客户当前几年的业务发展需求,
    正在被跟刚大客户所接受。这些功能模块是产品占领市场的动力,是续成熟的功能模块后,产品的增
    长动力。
    ?? 研究中的下一代产品方向:指的是还没有推出市场,正在研究中的,符合未来行业五到十年发展方向
    的模块。当然如果能创造出未来的发展方向,则是最高境界。如任天堂的Wii,苹果公司的Iphone。
    一个行业软件产品要保持长期的生命力,在整个产品的生命周期架构规划中,需要考虑到这三种模块和特
    性,只有这样才能保持产品的先进性和长久生命力。
    敏捷开发也强调拥抱市场变化,这对产品架构师提出了很高的要求– 深厚的业务背景,创新能力,技术
    洞察力,和架构思想。
    2) Scrum Master
    Scrum Master 虽然是敏捷开发的新名词,但是工作内容和开发组长没什么太大的区别。安排任务,协调资
    源,进度,解决难题。
    架构师根据对行业的理解和创新,设计出产品的架构。架构中包含的组件,以及不同组件之间的关系。Scrum
    Master 则是去进一步分解和实现这个架构中的每个组件。如果形容“奥运会开幕式”是一个产品,架构师则设
    计整个开幕式的主题,创意,架构和包含的主要环节,而Scrum Master 就是整个盘大工程的详细设计者和协调
    者。
    在我们产品中有三个Scrum Master,各自负责架构中的不同模块,并和开发人员一起把模块分解成一个一
    个单独的、可以衡量的用例,然后协调开发人员高质量的完成任务。
    此外,每天早上需要主持小组成员进行一个10 分钟左右Scrum 会议。每个组员汇报昨天完成的任务,是
    否完成任务以及碰到的问题,最后是今天打算完成的任务。Scrum Master 会协调解决每天碰到的问题,确保产
    品进度和质量。
    4. 总结
    在作者的理解中,敏捷开发是一种思维方式和软件过程方法论,以及一系列的最佳实践,它能帮助团队开
    发出更加符合市场需求的产品。我们团队在产品两个版本的敏捷开发历程中,不断的摸索,找到了一条适合自
    己的产品敏捷开发流程,我们还需要继续用敏捷的思想改进我们的敏捷开发流程,和大家讨论探索,持续改进。
  • RFT: Command-line

    2011-02-25 12:17:09

    RFT 自身提供了 command-line 的功能,可以实现大部分RFT界面可以实现的功能,如:

  • Compile a script.
  • Play back a script, passing command-line arguments to the script.
  • View and edit verification point and object map files
  • Invoke the Java/HTML enabler
  • Invoke the Application Configuration Tool
  • RFT帮助文档中,对这方面也进行了深入阐述,通过command-line 也可以实现无人值守的批处理执行RFT Scripts。

    Core command line format

    The core command line formats follow:

    java <standard java options> -classpath rational_ft.jar com.rational.test.ft.rational_ft <Functional Tester options> or
    java <standard java options> -jar rational_ft.jar <Functional Tester options>

    The standard java options refer to the Java™ command line options such as -classpath <classpath> to set the classpath appropriately.

    -classpath
    If you use the first command-line format, you must explicitly include the rational_ft.jar in the classpath. It can be found in the Functional Tester install directory. If you use the second command-line format, specify the full path of the rational_ft.jar file after the -jar option. You do not need to specify a classpath or the class to run (com.rational.test.ft.rational_ft ). If you are using Functional Tester with PurifyPlus™, use the first command-line format.

    See the Functional Tester API Reference (com/rational/test/ft/rational_ft) for a full list of the command-line options.

    Commonly used options

    The following is the list of more commonly used options:

    -datastore <datastore directory>
    Use this option whenever a script. is specified, for example use it with -record or -playback.
    -record <script. name>
    Use this option to record a new script. (or in conjunction with -insertafter <line number> to insert recording into an existing script). The script. name is not a file name. It is a fully qualified class name using the dot (.) character to separate package/namespace and script. class name. You can use -record <script. name>to record Java or VB.NET scripts, depending on the project type.
    -playback <script. name>
    Use this option to play back a Java script.
    -edit <file> or -display <file>
    Use this option to edit or view a Functional Tester verification point or object map. The <file> can be a complete file name (with directory path). Use double-quotes if the name or path includes space characters.
    -enable
    Use this option to open the Enable Environments dialog box to enable a specific environment.
    -appconfig
    Use this option to open the Application Configuration Tool dialog box.
    -inspector
    Use this option to open the Test Object Inspector Tool dialog box.

    Command-line usage examples

    In these examples, -classpath must point to the rational_ft.jar files.

    Note: The <script-name> values use standard Java package or .NET namespace naming conventions such as package.MyScript or Namespace.MyScript.

    Record a new script.:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> [ -map <sharedmap>] [options] -record <script-name>

    Record into an existing script, inserting before or after a given line:

    java -classpath<classpath> com.rational.test.ft.rational_ft -datastore <directory> [ -insertbefore <line>] [ -insertafter <line>] [options] -map <sharedmap>] [options] -record <script-name>

    Compile a script.:

    Note: You must enable the Java environment before compiling a script. with this command. You must also install Java SDK and add the bin directory to the path.
    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> [options] -compile <script-name>

    Play back a script, passing command-line arguments <values> to the script.:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -log <logname> [options] -playback <script-name> [ -args <values>]

    Play back a script. that uses classes from other functional test projects:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -projectpath <reference-project-path> -playback <script-name>

    Play back a script. that has an associated datapool:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -iterationCount <iteration value> -playback <script-name>

    Record, compile, and play back a script.:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> [options] -record <script-name> -compile -playback [ -args <values>]
    Note: To playback your scripts with dynamic VPs, add -rt.interative true before -playback in the command line.

    Construct an empty script.:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -map <sharedmapname> [options] -create <script-name>

    Regenerate the helper file for a script.:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -helper <script-name>

    Regenerate all helper files for a datastore:

    java -classpath <classpath> com.rational.test.ft.rational_ft -regenHelpers <script-name>

    Display an object-map file:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -display <object-map filename>

    Display a verification-point file:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -display <verification point filename>

    Edit an object-map file:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -edit <object-map filename>

    Edit a verification-point file:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -edit <verification point filename>

    Create and edit a shared-object map:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -fromMap <object-map filename1> -createMap <object-map filename2>

    Merge a later version of an object map into a current (modified) version of the same map:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -from <object-map filename1> -to <object-map filename2> -original <object-map filename1> -mergeMap

    Compare an actual verification point result to an expected verification point result:

    java -classpath <classpath> com.rational.test.ft.rational_ft -datastore <directory> -baseline <baseline verification point filename> -compare <expected verification point filename> <actual verification point filename>

    Enable a configured browser, Java environment, or Eclipse platform.:

    java -classpath <classpath> com.rational.test.ft.rational_ft -enableName <browser/Java environment/Eclipse>

    Disable all configured browsers, Java environments and Eclipse platforms:

    java -classpath <classpath> com.rational.test.ft.rational_ft -disableall

    Run the Application Configuration Tool:

    java -classpath <classpath> com.rational.test.ft.rational_ft -appConfig <application name>

    Run the Test Object Inspector:

    java -classpath <classpath> com.rational.test.ft.rational_ft -inspector

    Run the Object Properties Configuration Tool:

    java -classpath <classpath> com.rational.test.ft.rational_ft -objectlibraryou

    Running Functional Tester in unattended batch mode

    If you have TestManager installed and you want to run Functional Tester unattended in batch mode with a TestManager log, you must include the following options on the rational_ft command line:

    -user username
    The TestManager user name for login.
    -password password
    Optional. The TestManager password for login. Do not use this if no password has been set.
    -project fullpath and full projectname
    The name of the TestManager project, which is preceded by its full path. This is case-sensitive.
    -build build
    The name of an existing TestManager build. See Note below.
    -logfolder foldername
    The name of an existing TestManager log folder (see Note below) or the file specification for an HTML or a text log. If you use the HTML or text log folder name, you do not need to specify the build.
    Note: If you want to create a TestManager log, make sure you create the TestManager build and the TestManager log folders first. If these two folders do not exist, execution will fail when you run Functional Tester unattended in batch mode.
    -log logname
    The name of the TestManager, HTML or text log. If you use the HTML or text log name, you do not need to specify the build.

    Examples:

    Execute a script. on Windows®

    	java -classpath "C:\IBM\RFT\FunctionalTester\bin\rational_ft.jar" 
    	com.rational.test.ft.rational_ft -datastore \\My_project\AUser\RobotJProjects -user admin -project
    	\\My_project\AUser\TestManagerProjects\Test.rsp -build "Build 1" -logfolder "Default" -log 	
    	"Al_SimpleClassicsA#1" -rt.log_format "TestManager" -rt.bring_up_logviewer true -playback 
    	basetests.SimpleClassicsA_01

    If you do not want TestManager to display the log after playing the script, you must also include the following option on the rational_ft command line:

    -rt.bring_up_logviewer false

    Command line usage in Linux

    The command line format is: /opt/IBM/RFT/jre/bin/java /opt/IBM/RFT/FunctionalTester/bin/rational_ft.jar<Functional Tester options>

    If you use the above command-line format, you must explicitly set the functional tester environment variables. Alternatively, you can use the Functional Tester scriptft_cmdline.

    Examples:

    Enable all environments in Linux®

    	/opt/IBM/SDP7.0/ft_cmdline -enable ALL

    Execute a script. with command line arguments on Linux

    /opt/IBM/SDp7.0/ft_cmdline -datastore 
    /opt/IBM/RFT/workspace/Project1 -log testscript. -playback Script2 -args arg1 arg2
  • [论坛] lr调用jar在vuser中可以运行,但是controller中却报错

    2010-01-11 19:47:40

    如题,错误如下:javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.DocumentBuilderFactoryImpl not found

    查找了很多地方,没有找到原因,很偶然的在一个帖子里面发现了上述错误,虽然不是loadrunner的。但还是拿来试了试。居然解决了这个问题。

    方法:在java vuser中的init中加上如下两句话:

    System.setProperty("javax.xml.parsers.DocumentBuilderFactory","com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
            System.setProperty("javax.xml.parsers.SAXParserFactory","com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
  • 使用 IBM Rational Functional Tester: 了解和使用 TestObject.find 方法

    2010-01-05 16:01:04

    TestObject.find 方法是 Functional Tester 的强大部分,可以使自动化的脚本更容易维护。本篇介绍性的文章向您展示了将 TestObject.find 引入到您的自动化框架中的简单方法。

    第 1 部分:TestObject.find 方法的基础

    TestObject.find 方法是一个 Java™ 方法,IBM® Rational® Functional Tester (RFT) 工具使用它在运行时,动态地在被测应用程序(application under test,AUT)中定位 TestObject。通过使用它,您可以避免不得不记录动作以向对象地图(对象地图(Object Map) ) 中添加 TestObject 对象。

    被映射的对象使用被存储的、静态的、识别属性和对象层次来验证,在回放过程中脚本使用了正确的控件。虽然使用被记录的对象 进行对象识别速度很快,但是更新属性却是费时的,特别是当您需要将对象的属性权值,或者文本属性变更为正则表达式(regular expression,Regex)值时。find 方法向您提供一种能够排除大多数来自于对象地图(对象地图(Object Map) ) 的被记录控件的选项。

    TestObject.find 方法在 RFT 的 6.X 版本中更加健壮。现在,性能几乎与使用映射对象的性能相同。







    使用 find 方法的好处

    使 用 find 取代对象地图(对象地图(Object Map) ) 的最好的原因中的一个是您可以很容易地变更存储在 Java 或属性文件中的控件识别属性。其优点是不必变更存储在对象地图(对象地图(Object Map) ) 中的识别属性,这样您就避免了使用对象地图(对象地图(Object Map) ) UI 做变更,或重新记录对象来进行更新的更加冗长且费时的方法。








    了解及使用find

    要 了解 find 方法以及它到映射对象的关系,考虑可能会改变的应用程序中的控件,因而您需要更新对象地图(对象地图(Object Map) )。有些简单的控件方法,例如一个应用程序可能在版本更新过程中会变更其用户命令按钮的标签。例如,也许您的 AUT 中包含一个对话框,这个对话框中含有一个标签为Open的按钮。在以前的版本中,按钮的标签为OK。对于下一个版本,按钮的标签又变回了Open,但会有一个标签为Open with…的新按钮。

    如果您将 OK按钮记录到对象地图(对象地图(Object Map) ) 中了,那么您将需要变更识别属性来适应标签变更,并且您将需要记录新的 Open with… 按钮。(对于此实例,我们将忽略对由于 UI 变更导致的应用程序中的对象层次的可能的变更。)

    接下来,假设此对话框还包含了其他按钮,例如CancelHelp。您可以看到按钮可以用标签来区分,即使未来它们在应用程序中以不同的顺序出现。因此,只要您能够用标签上的文字来区分这些按钮,那么您就不需要考虑索引(index)属性或其他任意属性。








    利用 ClassicsJavaA 来探究 find方法

    您还可以使用 RFT 来获取关于随后您也许需要寻找的控件的信息。对于此例,使用 RFT 提供的 ClassicsJavaA 示例应用程序,并依照以下步骤。

    1. 当您在定义按钮的属性时,创建一个临时的空的 Functional Test 脚本来保留对象地图(Object Map) 。
    2. 在空测试脚本里的菜单中,选择Script. > Open Test Object Map
    3. 然后选择Applications > ClassicsJavaA 启动应用程序。
    4. 单击 Place Order 按钮。
    5. 单击 Member Logon 对话框中的 OK 按钮。

    现在应该出现 Place an Order 对话框。您可以使用该对话框来观察如何使用find 方法在对话框中定位按钮,以及为每个控件获得一个 TestObject。


    图 1. ClassicsJavaA 应用程序中的 Place an Order 对话框
    ClassicsJavaA 应用程序中的 Place an Order 对话框

    您将会在下一个对话框中看到三个按钮:Related Items、Place Order,和 Cancel。观察对象地图(Object Map)中出现的这三个按钮,然后继续以下步骤:

    1. 在 Private Test Object Map 中,从菜单中选择Test Object > Insert Object(s)
    2. 将 Object Finder 控件拖到 Place an Order 对话框上,将其挪到标题栏上,以使整个对话框四周包围着一个红色方框,然后释放鼠标按钮。
    3. 现在,在 Insert a GUI Object into the Object Map 对话框中,选择 Include all available objects on this window 并单击Finish
    4. 单击 Place Order 按钮。

    您 现在的对象地图(Object Map)中应该有一个 javax.swing.JFrame. 对象。选择 JFrame. 控件,以便您可以看到对话框的 Recognition 属性。对于此控件,标签或文本是定义该控件的最重要的两个属性。第二重要的属性是控件的类,因为可能会显示出许多 JFrame. 对象,但是可能只有一个的标签为 Place an Order。继续以下步骤:

    1. 展卡层次结构,找到按钮标签。
    2. 选择 Cancel 按钮,以便您可以分析 RFT 用来在对象地图(Object Map)中描述按钮的 Recognition 属性。这里列出了四个属性,其中两个对定义正确的按钮是至关重要的:class 和 accessibleContext.accessibleName 属性(参见图 2)。

    图 2. 使用 ClassicsJavaA 应用程序的对象地图(Object Map)的实例
    使用 ClassicsJavaA 应用程序的对象地图(Object Map)的实例
    1. 找到正确的按钮。许多对话框中包含一个 Cancel 按钮,所以您首先需要找到包含正确的 Cancel 按钮的对话框 TestObject。如果您首先找到了正确的对话框,那么找到对话框中正确的按钮就更容易了。
    2. 回到您的空测试脚本中。因为对话框是高级对象,所以您可以通过定义 RootTestObject 来开始 TestObject 搜索。当您做完这些后,您就可以使用 find 方法来定位对话框 TestObject:
    RootTestObject root = getRootTestObject();

    您已准备好使用 find 方法来定位 Place an Order 对话框,利用窗口的类信息。

    注意:确保打印出您找到的所有 TestObject 的属性,这样您可以检查它们。

    您的命令应类似以下内容:

    	// get all test objects of type JFrame. (dialog)
    TestObject[] to = root.
    find(atDescendant("class", "javax.swing.JFrame"));

    // print dialog test object properties to console
    for (int i = 0; i < to.length; i++)
    {
    System.out.println (to[i].getProperties());
    }

    结果显示应该类似以下内容(除去了大部分属性,为了节省空间):

    	{height=600, displayable=true, undecorated=false, ..., 
    class=javax.swing.JFrame, title=Place an Order, …}

    接下来,使用 find 方法代替上面的代码,利用对话框中该窗口的标签文本来定位 Place an Order 对话框:

    	// get all test objects that have the title "Place an Order"
    TestObject[] to = root.
    find(atDescendant("title", "Place an Order"));

    // print test object properties to console
    for (int i = 0; i < to.length; i++)
    {
    System.out.println(to[i].getProperties());
    }

    在两种情况下,您会发现只有一个对象和被显示的属性是相同的。这是理想的情况。要提高每种情况下该结果的可能性,您可以将两个属性组合成一个 find 调用:

    	// get all test objects of type JFrame. (dialog) 
    // AND have the caption "Place an Order"
    TestObject[] to = root.
    find(atDescendant(("class", "javax.swing.JFrame",
    "title", "Place an Order"));

    既然您已经找到了包含带有您搜索的标签的按钮的控件,那么您就可以使用 find 来定位正确的按钮了:

    	// capture the correct dialog as a test object
    TestObject toPlaceAnOrderDialog = to[0];

    // reuse the test object array, finding all buttons on the
    // "Place an Order" dialog that have the caption "Cancel"
    to = toPlaceAnOrderDialog.
    find(atDescendant("class", "javax.swing.JButton",
    "text", "Cancel"));

    // verify that only one button was found
    System.out.println(to.length);

    // capture the correct button as a GuiTestObject
    GuiTestObject cancelButton = new GuiTestObject(to[0]);

    您可以从任何 TestObject 来调用 find方法。这依赖于您所选的对象,搜索只限于您所选择的对象下面层级的对象。

    在上面的实例中,代码使用 atDescendant 来定位按钮。然而,您可以有许多方式来使用 find

    • atChild 搜索 TestObject 所有直接的子对象。
    • atDescendant 寻找 TestObject 的所有子对象。
    • atList 让您指定一个 atChild、atDescendant,和 atProperty 对象的列表,这样您可以减小搜索范围。







    第 2 部分:使用 TestObject.find 方法的实际应用程序的实例

    试验各种属性将帮助您确定哪个属性在寻找应用程序中的对象时工作的最好。您一旦了解了 find 方法如何帮助您动态定义控件,您就可以开始编写 getter 方法了,这样您就可以不依赖被记录的对象地图(Object Map)来定位对象了。

    将控件定义为对话框的一部分

    例如,对于 Place an Order 对话框,您可能决定将控件定义为对话框的一部分。在这种情况下,您应首先找到该对话框,然后,到对话框中找您希望修改的控件。对于该情境,您的代码设计应该如下所示:

    	      
    public class PlaceAnOrder
    {
    public static GuiTestObject getDialog()
    {
    RootTestObject root = getRootTestObject();
    TestObject[] to = root.
    find(atDescendant("title", "Place an Order"));
    return new GuiTestObject (to[0]);
    }

    public static GuiTestObject getButtonCancel()
    {
    TestObject[] to = getDialog()
    .find(atDescendant("class", "javax.swing.JButton",
    "text", "Cancel"));
    return new GuiTestObject(to[0]);
    }
    }

    这里是一个说明如何在脚本中使用这些方法的实例:

    	public void testMain(Object[] args) 
    {
    // Find the Place an Order dialog
    GuiTestObject dialogPlaceAnOrder = PlaceAnOrder.getDialog();
    GuiTestObject cancelOrder = PlaceAnOrder.getButtonCancel();
    cancelOrder.click ();
    }








    为找到通用的对象定义方法

    要找到更多通用的按钮标签,例如,Cancel,您可能需要开发更多能够找到存在于您的应用程序中的任意按钮的通用方法。例如,您可以使用getButton("Cancel", placeAnOrderDialog),在此,来源是父窗口。对于第二个情景,您的设计应该类似于此实例:

    	public class ClassicsJavaUI 
    {
    public static GuiTestObject getButton(String buttonName,
    TestObject parent)
    {
    TestObject[] to = parent.find(SubitemFactory.atDescendant
    ("class", "javax.swing.JButton", "text", buttonName));
    return(new GuiTestObject(to[0]));
    }

    public static GuiTestObject getButton(String buttonName)
    {
    RootTestObject root = RationalTestScript.getRootTestObject();
    TestObject[] to = root.find(SubitemFactory.atDescendant
    ("class", "avax.swing.JButton", "text", buttonName));
    return (new GuiTestObject(to[0]));
    }

    public static GuiTestObject getDialog(String dialogName)
    {
    RootTestObject root = RationalTestScript.getRootTestObject();
    TestObject[] to = root.find(SubitemFactory.atDescendant
    ("class", "javax.swing.JFrame", "title", dialogName));
    System.out.println (to.length);
    return (new GuiTestObject (to[0]));
    }
    }

    public class PlaceOrderWindow
    {
    public static GuiTestObject getDialog()
    {
    return ClassicsJavaUI.getDialog("Place an Order");
    }
    public static GuiTestObject getButtonPlaceOrder()
    {
    return ClassicsJavaUI.getButton("Place an Order", getDialog());
    }
    }

    这里是一个您可以如何在脚本中使用这些方法的实例,或者更适当地说,利用定义了 Place An Order 对话框的类:

    	public void testMain(Object[] args) 
    {
    GuiTestObject dialog = PlaceOrderWindow.getDialog();
    GuiTestObject buttonPlaceOrder =
    PlaceOrderWindow.getButtonPlaceOrder();
    buttonPlaceOrder.click ();
    }

    您测试的应用程序将确定您为寻找控件如何定义方法。下面部分是一个实际的例子,说明了系统是如何围绕 find 方法而设计的。







    在基于 Eclipse 的应用程序中实际使用 RFT find 方法

    在 测试基于 Eclipse 的应用程序中使用 find 方法的这个实例仅局限于 AUT 的一个部分,作为得出对象 getter 方法的设计的一个途径。本应用程序中的对象具有可识别的模式。标准的 SWT 对象是主要被使用的,但还有应用程序开发团队提供的一些专门的定制类。

    测试开发人员创建一个通用的方法来获取一个特定类的所有对象,或者获取一个具体父对象下给定索引号的一个对象。要使得自动化在应用程序的局部版本上工作,测试开发人员决定使用对象索引作为寻找对象的关键属性,而不是文本。下面介绍的类将用于做这件事。

    注意:这些类中的所有方法都是静态的,所以测试开发人员不需要将这个类实例化为一个对象就可以使用这些方法。可以通过使用以下格式来调用它们:<class name>. <method name> (<parameters>)

    FindAppObjects.java 类可以让您使用两个不同的方法:getAllObjects 和 getObjectAtIndex 。这些方法通常不直接使用,但它们是其他方法的基础。然而,您可以直接使用它们来确定通过哪些索引号找到哪些对象。

         
    /**
    * Given a parent object, find all descendant test objects with a
    * given class.
    * @param parent TestObject
    * @param className String
    * @return Array of TestObject or null if no objects of type
    * className were found.
    */
    public static TestObject[] getAllObjects(TestObject parent,
    String className)

    getAllObjects 将 TestObject 类型的父对象和 String 类型的字符串为输入,并返回包含了父对象的具体类型的所有子对象的 TestObject 的数组。此实例返回 RootTestObject 的所有子对话框:

    RootTestObject root = getRootTestObject ();
    TestObject[] dialogs =
    FindAppObjects.getAllObjects(root, "javax.swing.JFrame");

    选择正确的父 TestObject 对象是重要的,以便您可以从结果中只获得您正在寻找的对象。作为一个实例,假设下面的部分表示 AUT 中的一个对象层次:

    application
    composite
    group0
    button0
    button1
    group1
    button0
    button1

    Calling getAllObjects (application, "button") 将返回一个含有四个按钮的数组。然而,如果不循环扫描该数组,并打印出每个索引号和 button[index].getProperty("text") 字符串的话,就不能很快确定哪些索引号匹配哪些按钮。

    要取代这种麻烦的过程,进行一个以上的分层调用更有意义:

    // get all groups in the application
    TestObject[] groups = FindAppObjects.getAllObjects
    (application, "group");

    // limit search to only buttons in the first group found
    TestObject[] buttons = FindAppObjects.getAllObjects
    (groups[0], "button");

    // click the first button found under the group
    new Button(to[0]).click();

    getAllObjects (group0, "button") 调用只返回两个按钮,它们很可能处于正确的顺序(button0,然后 button1),如屏幕上显示的。如果整个应用程序或对话框中只有一些按钮,那么获得所有的按钮并且计算出索引可能会更容易。

    /**
    * Given a parent object, find the descendent test object
    * with a given class, at a given one-based index.
    *
    * @param parent TestObject
    * @param className String
    * @param index int, zero-based index
    * @return TestObject or null if index is greater than
    * the total number of objects found.
    */
    public static TestObject getObjectAtIndex
    (TestObject parent, String className, int index)

    getObjectAtIndex 精炼了 getAllObjects 的使用。它获取一个父的 TestObject 对象、一个 String 类的字符串对象,和一个以零为基数的整数,然后返回一个单个的 TestObject 对象。例如,此代码返回指定(父)分组下的第一个按钮:

              
    TestObject to = getObjectAtIndex(group1, BUTTON, 0);

    这 些方法都返回一个 TestObject 对象,并且那些可能不是您需要找的具体类型的对象。上面的两个方法都返回一般的 TestObject 对象。常常,您希望使用具体类型的对象。当使用这些方法时,确保在您将对象用于代码中之前,显式地将所返回的 TestObject 对象转换为正确的类型。

    FindBasicAppObjects.java 类提供进一步的细化和辅助。它是 FindAppObjects.java 的子类,并且使用了 getObjectAtIndex 方法。它包含返回 TestObject 父对象的索引号位置的子对象的 getter 方法。这时产品对象已经被转换成为了正确的类,所以您不需要自己再次的转换它。此实例返回标准的 Eclipse SWT widget 类的一个子集,例如 org.eclipse.swt.widgets.Button:

         
    /**
    * Find the index-specified org.eclipse.swt.widgets.Button
    * child control under the specified parent.
    *
    * @param parent TestObject
    * @param index int, zero-based
    * @return WButton
    */
    public static WButton getButton(TestObject parent, int index)

    getDialog 是 FindBasicAppObjects.java 中唯一一个不需要父亲对象的方法,因为它假设父对象是 AUT。为了能够识别正确的对话框,即使该对话框的标题是易变的,getDialog 将正则表达式(Regex)对象作为参数,如下所示:

         
    /**
    * Find a org.eclipse.swt.widgets.Shell control
    * with a caption specified by the given
    * Regular Expression (Regex).
    *
    * @param dialogCaption Regex object containing dialog's
    * expected caption
    * @return WFrame
    */
    public static WFrame. getDialog (Regex dialogCaption)

    比较 Place an Order 对话框的代码(在此部分的开头)和在下面实例中使用FindAppObjectsFindBasicAppObjects 的同样的行为。假设 Place an Order 对话框是 WFrame. 类型的 Eclipse 对话框,并且按钮是 WButton 类型的。

         
    public class PlaceAnOrder extends RationalTestScript
    {
    private final int CANCEL = 2;
    private final Regex DIALOG_CAPTION = new Regex
    ("^Place an Order$");

    public WFrame. getDialog()
    {
    return FindBasicAppObjects.getDialog(DIALOG_CAPTION);
    }

    public WButton getButtonCancel()
    {
    return FindBasicAppObjects.getButton(getDialog(),CANCEL);
    }

    public void testMain(Object[] args)
    {
    getButtonCancel().click();
    }
    }

    前面的代码是以很容易维护的方式编写的,代码中每个部分都分隔为容易理解的一块。对于喜欢更加紧凑布局的编码人员,该代码还可以以此方式书写:

         
    public class PlaceAnOrder extends RationalTestScript
    {
    public void testMain(Object[] args)
    {
    FindBasicAppObjects.
    getButton (FindBasicAppObjects.
    getDialog (DIALOG_CAPTION), CANCEL).click();
    }
    }









    总是编写单元测试

    除了基于 find的 getter 方法以外,前面提到的 FindBasicAppObjects.java 类还包含每个已定义的对象类型的基本的单元测试方法。每个单元测试方法获取一个具体类的对象和一个字符串(字符串应该是您在测试的 getter 方法的名称)。该名称被发送到控制台,以跟踪通过或是失败的结果。

    单元测试方法首先验证所提供的对象不是空的,然后对该对象执行一个或多个非常基础的操作。例如,WButton 对象的单元测试方法打印出该对象的文本属性(标签)。您可以看到 getter 方法是否返回正确的按钮。

    推 荐使用单元测试方法来测试每个 getter 方法。单元测试常常被视为多余的工作,但在对应用程序的用户界面(UI)做任何变更之后,运行单元测试将验证方法能继续找到 AUT 中正确的控件,并将确认 getter 方法中的任何代码变更能正确工作。运行单元测试确保测试用例能继续找到并返回正确的对象。

          /**
    * Unit test for button controls.
    *
    * @param b
    * @param methodName Name of method under test
    */
    public static void verifyButton(WButton b, String methodName)

    调用单元测试方法的实例:

          FindBasicAppObjects.
    verifyButton(getButtonCancel(),"getButtonCancel");








    局限性和建议

    总是在使用控件之间找到它,不要依赖于代码中存储的 TestObject。您找到的对象可能在您第一次找到它时和您下次需要使用它时之间变更了(或者删除并重新生成了)。每次您处理一个对象时,都需要调用find 方法来确保您得到正确的对象。

    如果您使用 find 方法,您不能记录脚本。因为您的 TestObject 不是在对象地图(Object Map)中定义的,在记录中,记录器不能挑出正确的对象名称。您仍旧可以对您的脚本所需的基本结构代码进行记录,但您需要用基于 find 的 getter 方法来取代所记录的被映射控件的 getter 方法。

    许多对象可能会显示出一样的结果。当您调用 find 方法时,可能会存在一个以上的 OK 按钮。确保通过使用不同的属性,例如 OK 按钮所属于的对话框,来获得正确的按钮。。将 TestObject 的 getProperties 方法的结果打印到控制台上,以观察哪个属性可以帮助您在类似的对象中区分出您所找的对象。

  • 数据统计

    • 访问量: 9226
    • 日志数: 16
    • 建立时间: 2008-12-09
    • 更新时间: 2014-05-04

    RSS订阅

    Open Toolbar