qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

软件验收管理工作内容

 目前,国内软件项目的验收管理没有可参照的强制内容及标准,这样,对于软件项目验收来说,存在很大分歧和不确定性,也为全程软件质量保障安插了隐患。

  为此,我们在参考了大量文献基础上,结合实际验收管理经验,总结了软件验收管理的部分工作内容,不足之处,欢迎指正。

  软件项目验收管理工作内容:

  一、软件验收管理的准备工作

  1、对完整产品的运行进行确认;

  2、证实系统已满足合同规定的条件及需求说明书中对系统功能和性能的要求;

  3、整理验收需要文档列表、系统软硬件配置清单:

  (1)软件工程立项批准文件

  (2)项目验收申请报告;

  (3)招标书

  (4)投标书

  (5)中标通知书

  (6)合同(含预算表)

  (7)软件需求说明书;

  (8)概要设计说明书;

  (9)数据及数据库设计要求说明书;

  (10)详细设计说明书;

  (11)操作手册;

  (12)用户手册

  (13)项目用户评价过程意见;

  (14)软件接口规范;

  (15)原代码或安装盘;

  (16)专家组要求的其他材料

  4、制定人员培训和技术支持的计划。

  二、软件系统验收申请报告

  1、卖方向买房提出软件系统验收申请报告,说明申请系统验收的准备情况和系统所具备的验收条件。系统验收申请报告,必须按合同书的有关规定,交付有关产品资料,其中包括系统设备及系统软件配置清单、文档、技术总结报告和测试分析报告等,系统验收申请报告应有卖方的技术负责人签字。

   2、买方的经办人或第三方验收管理机构必须了解验收系统的功能、性能和系统配置与文档等方面的要求,掌握合同书中规定的系统验收条款,对卖方提交的系统 验收申请报告进行审查,提出处理意见;买方技术负责人经审查后,在申请报告上签字并对卖方的申请进行答复;买方将按合同有关条款做好系统验收的全部准备工 作,包括对测试用例、测试数据、测试过程和测试环境的准备。

  三、制定系统验收计划

  在软件系统验收活动进行之前,应制定一套完整的系统验收测试计划。该计划要由买方认可,而且还要包括一些由买方提供的测试方案,该计划应包括系统验收工作的活动程序、验收测试要求、技术条件、设备资源、验收准则、工作人员的组成以及日程安排等内容。

  四、制定组织机构及人员组成

  1、成立专门的软件系统验收委员会,作为系统验收的组织机构,委员会设主任委员若干,并由该委员会组织成立系统验收测试组、技术组和文档审查组,配备若干测试员和记录员。

  2、验收委员会由买方代表、特邀专家或第三方测试机构专家及最终买方代表组成,必要时,也可吸收卖方代表参加。特邀专家必须是行业信息领域的权威,熟悉国内外该领域技术发展的状况。

  五、规范验收委员会的任务与权限

  1、验收委员会的任务

  审定系统验收计划;

  听取卖方的《技术总结报告》和《测试分析报告》;

  判定所验收的系统是否符合合同及系统需求说明书的要求;

  审定验收测试计划;

  组织验收测试和进行系统验收评审,并形成系统验收报告;

  监督系统验收后的产品移交。

  2、验收委员会的权限

  有权要求卖方和买方对系统开发过程中的有关问题进行说明,提出质疑并要求作出解释;

  在验收过程中,协调卖方和买方之间可能发生的纠纷;

  决定系统是否通过验收。

  六、验收记录

  验收工作的全部过程必须详细记录,记录验收过程中验收委员会,提出的所有问题与建议,卖方的解答和验收委员会对被验收系统的评价,并形成文件供评审时查阅及存档。

  七、制定软件验收测试计划

  软件系统的验收测试是系统验收活动中最关键的步骤,被验收的系统必须满足合同条款与系统需求说明书中规定的要求。

  测试计划及程序包括下列几项:

  1、测试计划及程序的目的;

  2、各项功能测试所需输入的数据;

  3、测试结果记录的说明;

  4、观察、测试结果的设备、工具及程序;

  有关的测试结果要以书面报告的形式由卖方向买方提交,

  内容包括:

  (1)测试的系统功能;

  (2)为纠正系统缺点需做的变动;

  (3)为提高系统性能提出的建议。

 八、软件系统验收

  1、系统设备验收:

  验收委员会的技术组根据卖方提供的系统设备、计算机网络设备、和系统软件、数据库和工具软件配置清单,并对照合同或项目建议书的有关规定,检查这些设备及其各项性能指标是否符合要求并将审查结果写进《系统设备验收报告》。

  2、验收测试前的检查:

  验收委员会在审定系统验收测试计划时,要检查测试环境是否符合要求,检查全部测试项目的测试用例是否准备好,有关测试人员是否全部到位。

  3、系统演示:

  卖方应向验收委员会演示被验收系统的全部买方界面、系统包括的主要功能、性能,以证明系统实现的功能与合同要求一致。通过演示活动让验收委员会 成员对系统有一个直观和概括的了解。验收委员可现场选用实例对被验收系统时行演示考核,以证实与系统需求的一致性、程序和文档的一致性。

  4、验收测试:

  验收测试组应按系统验收测试计划对系统进行:1)环境验收测试;2)可靠性验收测试;3)维护性验收测试;4)功能验收测试;5)稳定性验收测 试;6)性能验收测试;7)仿真测试;8)可移植性、兼容性、可靠性、错误的恢复功能等测试;9)文档验收。验收标准:1) 测试用例不通过数的比例< 1.5 %;2) 不存在错误等级为1 的错误;3) 不存在错误等级为2 的错误;4) 错误等级为3 的错误数量≤ 5;5) 所有提交的错误都已得到更正。

  测试员按分工分别对被验收系统进行逐项测试,并详细记录每一项测试结果,将这些结果分别与预期的结果对照分析,然后写出《系统验收测试报告》,该报告将作为验收委员会评价系统的主要依据,也是买方确定是否接收该系统的主要依据。

  5、系统验收评审:

  在软件验收测试完成以后,验收委员会应及时主持评审会,听取有关报告和审议验收结果,并对系统作出综合评价。评审会的议程如下:

  听取卖方的《测试分析报告》和《技术总结报告》;

  听取系统验收测试组的《系统设备验收报告》、文档审查组的《文档审查报告》及测试组的《系统验收测试报告》;

  按以下的验收准则对系统进行评价:

  (1)系统是否满足用户信息系统要实现的目标。

  (2)系统采用的技术和实现方案是否做到可靠、先时、灵活、实用。

  (3)设备选型是否达到以下要求:

  选用目前在国际上技术先进、性能优异的设备,确保系统的先进性和可靠性。

  所选用的设备不仅能满足目前用户信息系统业务在功能和性能上的要求,而且具有良好的开放性和扩充性。

  所选用的应用开发平台和开发工具先时、简便、有效。

  便于与其他系统的衔接,实现资源共享。

(4)运行系统的可靠性是系统建设的首要出发点。因此,要求卖方提供高可靠性的产品和技术,确保系统的安全和可靠。要求系统具有较强的容错能力,使系统不易崩溃。

  (5)关键系统设备与数据备份的设施是否达到安全可靠。

  验收委员会应进行认真地讨论,对被验收的系统给出实事求是的评价,内容包括系统的先进性、功能性、可靠性和安全保密性。最后由验收委员会进行表决,决定系统是否通过验收。

  6、软件系统验收报告:

  在验收评审后,验收委员会应写出“系统验收报告”,详尽地记录验收中对系统的评价及验收意见,尤其要明确系统在验收中发现的问题和缺陷,以及需 要改进的意见和卖方对些所作的承诺,验收委员会全体成员在验收报告上签字,根据验收委员会表决情况,由验收委员会主任在验收报告上签署验收意见。

  验收结论分为以下两种;

  a.通过。表示同意验收的委员超过三分之二;

  b.不通过。表示同意验收的委员不超过三分之一。

  如果系统验收不能通过,验收委员会将根据合同书的规定与供需双方协商处理意见,可能的结果是:要求卖方限期完成开发任务,重新提出验收申请或者 终止合同。系统验收通过后,要确定系统进入试运行的时间结束时间,明确卖方在试运行期间要解决的遗留问题以及改进系统的意见,对此卖方的代表要作出承诺。

  7、软件项目产品移交:

  系统通过验收以后,验收委员会的技术组和文档审查组应分别卖方提供的系统设备清单和文档资料清单进行验收,逐项核实后移交给买方。移交结束后形成产品移交文件,该文件应包括以下内容:

  a.移交产品清单

  卖方向买方移交的产品清单分别包括系统构成的硬件和软件部分。硬件部分包括卖方在应和开发或系统配置时,受买方委托代购的各种计算机与外部设 备,移交清单中应包括设备名称、单价、数量、供货厂商、保修期限;软件部分包括系统软件、数据库软件等软件的名称、单价、供货厂商。

  b.移交的时间、地点、收授人签字。

版权声明:本文出自山东省软件评测中心 张凯丽,51Testing软件测试网原创出品,未经明确的书面许可,任何人或单位不得对本文进行复制、转载或镜像,否则将追究法律责任。

http://www.51testing.com

posted @ 2012-12-13 10:17 顺其自然EVO 阅读(472) | 评论 (0)编辑 收藏

跟屌丝一起学习 DB2 第五课 存储过程(三) 存储过程实例

  客户在进行短信服务这个业务申请时,需要填写一些基本信息,然后根据这些信息判断这个用户是否已经存在于业务系统中。因为网上服务和业务系统两个项目物理隔离,而且网上数据库保存的客户信息不全,所以判断需要把数据交换到业务系统,在业务系统中判断。

    解决方式是通过存储过程,以前也了解过存储过程,但没使用到项目中。不过经过一番努力最后还是完成了,期间遇到了一些困难,特写此文让对DB2存储过程还不熟悉的童鞋避免一些无谓的错误。

   

复制代码
DROP PROCEDURE "PLName"
@
CREATE PROCEDURE "PLName"(--存储过程名字
IN IN_ID BIGINT , --以下全是输入参数
IN IN_ENTNAME VARCHAR(200) ,
IN IN_REGNO VARCHAR(50),
IN IN_PASSWORD VARCHAR(20),
IN IN_LEREP VARCHAR(300),
IN IN_CERTYPE CHARACTER(1),
IN IN_CERNO VARCHAR(50),
IN IN_LINKMAN VARCHAR(50),
IN IN_SEX CHARACTER(1),
IN IN_MOBTEL VARCHAR(30),
IN IN_REQDATE TIMESTAMP,
IN IN_REMITEM VARCHAR(300),
IN IN_STATE CHARACTER(1),
IN IN_TIMESTAMP TIMESTAMP
)
BEGIN

declare V_RESULT BIGINT; --声明变量
DELETE FROM TableNameA WHERE ID = IN_ID;

SET V_RESULT = NULL; --为变量赋值
  --检查用户输入的信息是否合法

select b.id INTO V_RESULT from TableNameB b,TableNameC c where 正常的判断条件
if(V_RESULT IS NOT NULL) then ---如果合法,执行下面的insert语句
INSERT INTO TableNameA(ID,ENTNAME,REGNO,PASSWORD,LEREP,CERTYPE,CERNO,LINKMAN,SEX,MOBTEL,REQDATE,REMITEM,STATE,TIMESTAMP)
VALUES(IN_ID,IN_ENTNAME,IN_REGNO,IN_PASSWORD,IN_LEREP,IN_CERTYPE,IN_CERNO,IN_LINKMAN,IN_SEX,IN_MOBTEL,IN_REQDATE,IN_REMITEM,IN_STATE,IN_TIMESTAMP);
end if;
commit;
END
@
复制代码

功能说明:

调用存储过程时会传入一些值(IN输入参数),然后根据传入的值查询数据库(select语句),根据查询结果执行操作(添加、删除、更新)

有两种方式执行写好的存储过程:

    1.拷贝到DB2客户端工具中直接执行

  特别注意:执行时将

改成@,之前很多错误都和它有关,比如:“该命令被当作 SQL
语句来处理,因为它不是有效的命令行处理器命令”正
是这个问题花费了很长时间,严重影响心情

    2.将上面的语句保存为test.db2文件放到任意目录下(比如D盘根目录),然后在cmd输入db2cmd 然后输入db2 -td@ -vf  D:\test.db2即可

 执行后就可以测试存储过程写的是否正确

 直接写sql:

call PLName(存储过程名字) (IN_ID,IN_ENTNAME,IN_REGNO,IN_PASSWORD,IN_LEREP,IN_CERTYPE,IN_CERNO,IN_LINKMAN,IN_SEX,IN_MOBTEL,IN_REQDATE,IN_REMITEM,IN_STATE,IN_TIMESTAMP对应的值)

以上就是我今天所用到的存储过程,功能非常简单,比较复杂的操作也在摸索阶段,有什么疑问大家可以随时交流。

CREATE PROCEDURE  KJZB.ZQINVEST_JT(out returnCode Integer,out 
errorMsg varchar(255)) 
LANGUAGE SQL 
BEGIN 
declare sql_code integer default 0; 
    declare SQLSTATE char(5) default '00000'; 
    declare SQLCODE integer default 0; 
    declare sqlMsg varchar(200) default ''; 
    declare stmt varchar(1024); 

    declare vCurdate    varchar(20);                   -- 当天日期 
    declare vCurYMd     varchar(6);                    -- 当天所在的年月 
    declare vCurDay  varchar(2) ;                         --所在天 
    declare maxday      integer default 30;            -- 每个月按30天算 
    declare fday         integer;                      -- 实际天数 
    declare lastday      integer;                      -- 到期日的天数 
    declare firstday     integer;                      -- 购买月的天数 
    declare firstmonth   integer;                      -- 购买的月份 
    declare lastmonth    integer;                      -- 到期日的月份 
    declare firstyear    integer;                      -- 购买日的年份 
    declare lastyear     integer;                      -- 到期年份 
    declare disday       integer;                      -- 间隔天数 
    declare dismonth     integer;                      -- 间隔月份 
    declare disyear      integer;                      -- 间隔年份 
    declare vLsh         varchar(32);                  -- 流水号 
    declare vjgh         varchar(8);                    -- 机构码 
    declare ywzh       varchar(32);                     -- 业务帐号 
    declare vzh         varchar(32);                     -- 投资账号 
    declare vJgbm       varchar(8) default '32022300';   -- 投资帐号对应的机构(默认为清算中心)

   declare vDfJgbm     varchar(8);                      -- 对方机构号 
    declare gmrq        varchar(8);                      -- 购买日期 
    declare dqrq        varchar(8);                      -- 到期日期 
    declare zqpz        varchar(1);                      -- 债券品种 
    declare hth         varchar(16);                     -- 合同号 
    declare fxfs        varchar(1);                      -- 付息方式 
    declare ywlx        varchar(3) default '888';        -- 业务类型(默认业务类型 888) 
    declare tzqx        varchar(1);                      -- 投资期限 
    declare ysjlxkmkzz  varchar(7);                      -- 应收(计)利息科目控制字 
    declare tzsykmmkzz  varchar(7);                      -- 投资收益科目控制字 
    declare yjkmkzz     varchar(7) default '142112';     -- 溢价科目控制字 长期投资(溢价142112) 
    declare zjkmkzz     varchar(7) default '142113';     -- 折价科目控制字 长期投资(折价142113) 
    declare fykmkzz     varchar(7) default '142111';     -- 相关费用科目控制字 长期投资(相关费用 142111) 
    declare zid         varchar(32) default '0';         -- id 债券投资ID 
    declare vId         varchar(32);                     -- id 债券流水ID 
    declare yslx        double;                       -- 应收利息 (面值 * 票面利率 (年) /360 * 当月实际天数) 
    declare yjlx        double;                       -- 应计利息 (面值 * 票面利率 (年) /360 * 当月实际天数) 
    declare ysjlx       double;                       -- 应收计利息 
    declare pmje        double;                       -- 票面金额 
    declare pmll        double;                       -- 票面利率 
    declare tzsy        DECIMAL(12, 2);                -- 投资收益       ( 应收(计)利息 - 每月摊销的溢价 - 每月摊销相关费用 ) 
    declare txyj        double;                       -- 月摊销溢价     ( 溢价/(到期日 - 购买日 + 1) * 当月实际天数 ) 
    declare txzj        double;                       -- 月摊销折价     ( 折价/(到期日 - 购买日 + 1) * 当月实际天数 ) 
    declare txfy        double;                       -- 月摊销费用     (相关费用)/(到期日 - 购买日 + 1) * 当月实际天数 
    declare yj          double default 0.0;           -- 溢价 
    declare zj          double default 0.0;           -- 折价 
    declare fy          double default 0.0;           -- 相关费用 

 

    declare zqtz cursor for s1;   建立游标
    -- 声明异常 
    DECLARE CONTINUE HANDLER FOR NOT FOUND,SQLEXCEPTION,SQLWARNING 
    begin 
       set sql_code   = SQLCODE; 
       set returnCode = sql_code; 
    end; 

    set vCurdate = (select char(current_date) from sysibm.sysdummy1); 
    set vCurdate = replace(vCurdate,'-',''); 
    set vCurYMd  = substr(vCurdate,1,6); 
    set vCurDay  = substr(vCurdate,7,2); 

   -- 读取还没到期的长期债券投资信息 
   set  stmt = ' select JGBM,ZH,HTH,ZQPZ,PMJE,LL,GMRQ,DQR,YSLX,YJLX,YJ,ZJ,FY,FXFS,id,cast(substr(GMRQ,7,2) as integer),cast(substr(DQR,7,2) as integer),cast(substr(GMRQ,5,2) as integer),cast(substr(DQR,5,2) as integer),cast(substr(GMRQ,1,4) as integer),cast(substr(DQR,1,4) as integer)  from kjzb.zqinvest where substr(dqr,1,6) >=''' ||  vCurYMd  || ''' and tzqx = ''2'' and tzzt <> ''2'''; 
   prepare s1 from stmt; 

    open zqtz; 
    fetch zqtz into vDfJgbm,vzh,hth,zqpz,pmje,pmll,gmrq,dqrq,yslx,yjlx,yj,zj,fy,fxfs,zid,firstday,lastday,firstmonth,lastmonth,firstyear,lastyear; 
    while sql_code <> 100 do 
        -- 如果是31日购买的,第一个月不计提 
        if ( firstday = 31 and substr(gmrq,1,6) = vCurYMd ) then 
            goto EXIT; 
        end if; 

-- 如果是01日到期的,最后一个月不计提 
if ( lastday = 1 and substr(dqrq,1,6) = vCurYMd ) then 
    goto EXIT; 
end if; 

        -- 间隔年份 
        if ( firstyear = lastyear ) then 
            set disyear = 0; 
        else 
            set disyear = lastyear - firstyear - 1; 
        end if; 

        -- 同年 
        if ( disyear = 0 ) then 
            -- 下个月或同月(业务逻辑上不会发生,但程序上要判断防止业务人员误操作) 
            if ( lastmonth - firstmonth <= 1 ) then 
            set dismonth = 0; 
            else 
                set dismonth = lastmonth - firstmonth - 1; 
            end if; 
        else 
            -- 不在同一年,间隔月份 = 第一年的月份 + 中间年的月份 + 最后一年的月份 
            set dismonth = ( 12 - firstmonth ) + ( disyear * 12 ) + ( lastmonth - 1 ) ; 
        end if; 
   -- 初始化摊销溢价、摊销折价、摊销费用 
        set txyj = 0.0; 
        set txzj = 0.0; 
        set txfy = 0.0; 
     if  fxfs  = '5'  then                              -- 根据付息方式来计应计利息还是应收利息科目控制字 
      if zqpz = '1' then 
      set ysjlxkmkzz = '142107';               -- 应计利息 国债     142107 
      set tzsykmmkzz = '514111';               -- 投资收益(债券利息收入-国债利息收入)   514111 
      end if; 
      if zqpz = '2' then 
      set ysjlxkmkzz = '142108';               -- 应计利息 金融债   142108 
      set tzsykmmkzz = '514112';               -- 投资收益(债券利息收入-金融债券利息收入) 514112 
      end if; 
      if zqpz = '3' then 
      set ysjlxkmkzz = '142109';            -- 应计利息 公司债   142109 
      set tzsykmmkzz = '514113';               -- 债券利息收入-企业债券利息收入 514113 
      end if; 
      if zqpz = '4' then 
      set ysjlxkmkzz = '142110';            -- 应计利息 其他债券 142110 
      set tzsykmmkzz = '514114';               -- 债券利息收入-其他债券利息收入   514114 
      end if; 
   else 
       if zqpz = '1' then 
      set ysjlxkmkzz = '132102';               -- 应收利息 国债     132102 
      set tzsykmmkzz = '514111';               -- 投资收益(债券利息收入-国债利息收入)   514111 
      end if; 
      if zqpz = '2' then 
      set ysjlxkmkzz = '132103';               -- 应收利息 金融债   132103 
      set tzsykmmkzz = '514112';               -- 投资收益(债券利息收入-金融债券利息收入) 514112      
      end if; 
      if zqpz = '3' then 
      set ysjlxkmkzz = '132104';            -- 应收利息 公司债   132104 
   set tzsykmmkzz = '514113';               -- 债券利息收入-企业债券利息收入 514113     
      end if; 
      if zqpz = '4' then 
      set ysjlxkmkzz = '132105';            -- 应收利息 其他债券 132105 
      set tzsykmmkzz = '514114';               -- 债券利息收入-其他债券利息收入   514114 
      end if;       
   end if; 
  
   if ( substr(dqrq,1,6) = vCurYMd ) then           -- 最后一个月计提 
       set fday = lastday - 1; 
   else 
       if ( substr(gmrq,1,6) = vCurYMd ) then       -- 第一个月计提 
   set fday = 30 - firstday + 1; 
       else 
       set fday = maxday;                        -- 平常按30天算 
       end if; 
   end if; 
-- 实际天数 = 第一个月的天数 + 间隔月份 * 30 + 最后一个月的天数(到期日不算) 
        set disday =  ( 30 - firstday + 1) + ( dismonth * 30 ) + ( lastday - 1 );  
   
        --  记提公式:面值*票面利率(年)/ 360*当月实际天数 
        set ysjlx = pmje * (pmll / 100) /360 * fday; 
        -- 投资收益公式 : 应收(计)利息 ( + 月摊销折价)— 月摊销溢价 - 月摊销费用 
        set tzsy = ysjlx; 
        -- 设计思想为如果有溢价,则应该没有折价 
       if yj <> 0.0 then 
           set txyj = yj /disday * fday; 
           set tzsy = tzsy - txyj; 
       else 
           if zj <> 0.0 then 
set txzj = zj/disday* fday; 
set tzsy = tzsy + txzj;      
           end if; 
 if fy <> 0.0 then 
           set txfy = fy /disday * fday; 
           set tzsy = tzsy - txfy; 
       end if; 
        --借: 1321 应收利息[按月计提金额] 
   set vId =  kjzb.getvId(); 
   set vLsh = kjzb.getLsh(32);                       -- 获取流水号 
        set vjgh = kjzb.getJgh(vzh);                      -- 获取机构号 
        set ywzh = kjzb.getZh(vjgh,ysjlxkmkzz);           -- 应收(计)利息账号 
        if ywzh is null then 
               set sqlMsg = '债券记提走帐时找不到指定机构['||vjgh||']和科目控制字为['|| ysjlxkmkzz ||']的帐号'; 
               set errorMsg =  sqlMsg; 
               goto ERROR_RETURN; 
        end if; 
        insert into kjzb.FLLSB values(vId,vJgbm,ysjlxkmkzz,vLsh,hth,ywzh,'借',ysjlx,'','0000',vCurdate,ywlx); 
        if sql_code <> 0 then 
           set sqlMsg = '债券走帐插入会计分录出错'; 
           set errorMsg =  sqlMsg; 
           goto ERROR_RETURN; 
        end if; 
   
   -- 借: 1421 长期投资(折价)[按月计提金额] 
   if zj <> 0.0 then 
      set vId =  kjzb.getvId(); 
           set ywzh = kjzb.getZh(vjgh,zjkmkzz);          -- 长期投资(折价)账号 
   if ywzh is null then 
       set sqlMsg = '债券记提走帐时找不到指定机构['||vjgh||']和科目控制字为['|| zjkmkzz ||']的帐号'; 
       set errorMsg =  sqlMsg; 
    goto ERROR_RETURN; 
   end if; 
           insert into kjzb.FLLSB values(vId,vJgbm,zjkmkzz,vLsh,hth,ywzh,'借',txzj,'','0000',vCurdate,ywlx); 
           if sql_code <> 0 then 
             set sqlMsg = '债券走帐插入会计分录出错'; 
             set errorMsg =  sqlMsg; 
             goto ERROR_RETURN; 
           end if; 
   end if; 
   
        -- 贷: 5141 投资收益  应收(计)利息-每月摊消的溢价-每月摊消相关费用 
   set vId =  kjzb.getvId(); 
        set ywzh = kjzb.getZh(vjgh,tzsykmmkzz);           -- 投资收益账号 
if ywzh is null then 
     set sqlMsg = '债券记提走帐时找不到指定机构['||vjgh||']和科目控制字为['|| tzsykmmkzz ||']的帐号'; 
    set errorMsg =  sqlMsg; 
    goto ERROR_RETURN; 
end if; 
        insert into kjzb.FLLSB values(vId,vJgbm,tzsykmmkzz,vLsh,hth,ywzh,'贷',tzsy,'','0000',vCurdate,ywlx); 
        if sql_code <> 0 then 
           set sqlMsg = '债券走帐插入会计分录出错'; 
           set errorMsg =  sqlMsg; 
           goto ERROR_RETURN; 
        end if; 
        -- 贷: 1421 长期投资(溢价)[按月摊消]每月摊消的溢价(溢价/(到期日-购买日+1) *当月实际天数) 
        if yj <> 0.0 then 
      set vId =  kjzb.getvId(); 
        set ywzh = kjzb.getZh(vjgh,yjkmkzz);              -- 长期投资(溢价)账号 
if ywzh is null then 
    set sqlMsg = '债券记提走帐时找不到指定机构['||vjgh||']和科目控制字为['|| yjkmkzz ||']的帐号'; 
    set errorMsg =  sqlMsg; 
    goto ERROR_RETURN; 
end if; 
        insert into kjzb.FLLSB values(vId,vJgbm,yjkmkzz,vLsh,hth,ywzh,'贷',txyj,'','0000',vCurdate,ywlx); 
        if sql_code <> 0 then 
           set sqlMsg = '债券走帐插入会计分录出错'; 
           set errorMsg =  sqlMsg; 
           goto ERROR_RETURN; 
        end if; 
        end if; 
        -- 贷: 1421 长期投资(相关费用)[按月摊消金额] 每月摊消相关费用(相关费用/(到期日-购买日+1) *当月实际天数) 
        if (fy <> 0.0)   then 
        set vId =  kjzb.getvId(); 
        set ywzh = kjzb.getZh(vjgh,fykmkzz);              -- 长期投资(相关费用)账号 
if ywzh is null then 
    set sqlMsg = '债券记提走帐时找不到指定机构['||vjgh||']和科目控制字为['|| fykmkzz ||']的帐号'; 
    set errorMsg =  sqlMsg; 
    goto ERROR_RETURN; 
end if; 
        insert into kjzb.FLLSB values(vId,vJgbm,yjkmkzz,vLsh,hth,ywzh,'贷',txfy,'','0000',vCurdate,ywlx); 
        if sql_code <> 0 then 
           set sqlMsg = '债券走帐插入会计分录出错'; 
           set errorMsg =  sqlMsg; 
           goto ERROR_RETURN; 
       end if; 
        end if; 
   
   -- 对 kjzb.zqinvest 表的以下字段进行更新 
   -- yjtyjlx 【已计提应计利息】 
        -- yjtyslx 【已计提应收利息】 
        -- ytxyj   【已摊销溢价】 
        -- ytxzj   【已摊销折价】 
  if fxfs = '5' then 
            update kjzb.zqinvest set yjtyjlx = yjtyjlx + ysjlx,ytxyj = ytxyj + txyj,ytxzj = ytxzj + txzj,ytxfy = ytxfy + txfy where id = zid; 
            if sql_code <> 0 then 
        set sqlMsg = '债券走帐时更新表出错'; 
        set errorMsg =  sqlMsg; 
        goto ERROR_RETURN; 
           end if; 
        else 
            update kjzb.zqinvest set yjtyslx = yjtyslx + ysjlx,ytxyj = ytxyj + txyj,ytxzj = ytxzj + txzj,ytxfy = ytxfy + txfy where id = zid; 
            if sql_code <> 0 then 
        set sqlMsg = '债券走帐时更新表出错'; 
        set errorMsg =  sqlMsg; 
        goto ERROR_RETURN; 
           end if; 
       end if; 
   
        EXIT: 
         fetch zqtz into vDfJgbm,vzh,hth,zqpz,pmje,pmll,gmrq,dqrq,yslx,yjlx,yj,zj,fy,fxfs,zid,firstday,lastday,firstmonth,lastmonth,firstyear,lastyear; 
    end while; 
    close zqtz;                                    -- 关闭游标 
    COMMIT; 
    set sqlMsg   = '债券投资自动计提成功!'; 
    set errorMsg =  sqlMsg; 
    set sql_code = 0; 
    set returnCode = sql_code; 
    return 1; 
    ERROR_RETURN: 
        ROLLBACK; 
        return -1; 

 

posted @ 2012-12-12 15:17 顺其自然EVO 阅读(575) | 评论 (0)编辑 收藏

跟屌丝大哥一起学习设计模式---中介者模式

     摘要: 中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。通用类图:举例:在一个公司里面,有很多部门、员工(我们统称他们互相为Colleague“同事”),为了完成一定的任务,“同事”之间肯定有许多需要互相配合、交流的过程。如果由各个“同事...  阅读全文

posted @ 2012-12-12 13:23 顺其自然EVO 阅读(351) | 评论 (0)编辑 收藏

跟屌丝一起学习 DB2 第五课 存储过程(二)

REPEAT语句
ftch_loop2:
REPEAT
    FETCH  c1  INTO  v_firstname,  v_midinit,  v_lastname;
UNTIL  SQLCODE  <>  0  AND  REPEAT  ftch_loop2;
WHILE语句
WHILE  at_end  =  0  DO
    FETCH  c1  INTO  v_firstname,  v_midinit,  v_lastname;
    IF  SQLCODE  =  100  THEN
        SET  at_end  =  1;
    END  IF;
END  WHILE;
LEAVE和ITERATE语句
LEAVE和ITERATE语句来控制循环
LEAVE语句用来跳出循环
ITERATE语句用来回到for或者while循环的开始重新执行
示例
FETCH_LOOP1:  LOOP
    FETCH  c1  INTO  v_dept,  v_deptname,  v_admdept;
    IF  at_end  =  1  THEN  
        LEAVE FETCH_LOOP1;
    ELSEIF  v_dept  =  ‘D01’  THEN
        ITERATE FETCH_LOOP1;
    END  IF;
    INSERT  INTO  department(deptno, deptname, admdept)
   VALUES(‘NEW’, v_deptname, v_admdept);
END  LOOP FETCH_LOOP1;
GOTO语句
GOTO语句用于直接跳转到指定标签处。例如:
IF  v_DEPT  =  ‘D11’
    GOTO  bye;
……
bye:
RETURN语句
RETURN语句用于向调用返回。
IF  v_DEPT  =  ‘D11’
    RETURN  1;
八 游标和结果集

游标的声明
下面是游标声明的几个例子:
DECLARE  c1  CURSOR  FOR select * from staff;
(DECLARE关键字,cl游标名称, CURSOR是必须有的,;指通过c1的游标来操作staff里所有的数据)最常用的最普通的。
2.DECLARE  c1  CURSOR  WITH  HOLD FOR  select  *   form  staff;
3.DECLARE  c1  CURSOR WITH  RETURN  TO  CALLER  FOR  select  *   form  staff;
4.DECLARE  c1  CURSOR WITH  RETURN  TO  CLIENT  FOR  select  *   form  staff;

游标的相关操作
打开游标
OPEN  <游标名>
提取游标
FETCH  <游标名>  INTO  <变量列表>
关闭游标
CLOSE  <游标名>
游标的遍历

DECLARE  at_end  INT  DEFAULT  0; (声明了at_end的变量,默认值是0)
DECLARE  PIID  INTEGER  DEFAULT  0;
DECLARE  PINT  INTEGER  DEFAULT  0;
DECLARE  not_found  CONDITION FOR SQLSTATE '02000';   
DECLARE  c1  CURSOR  FOR  SELECT  IID  FROM  YH; (声明了一个游标,把IID的指标拿出来)
DECLARE  CONTINUE  HANDLER  FOR  not_found  SET  at_end  =  1;   
OPEN c1; (进行循环)
SET  PCOUNT  =  0;   
ins_loop:   LOOP
    FETCH  c1  INTO  PIID;   
    IF  at_end  <>  0  THEN   
        LEAVE  ins_loop;    ( LEAVE跳出循环)
    END  IF;
    SET  PCOUNT  =  PCOUNT  +  1; (表示提取了多少条记录)
END LOOP;
删除游标对应的数据行
DECLARE  cursor1  CURSOR  FOR   SELECT  DEPTNO,  DEPTNAME,  LOCATION
FROM  DB2ADMIN.ORG  FOR  UPDATE;(声明一个cursor1的游标,从一个表时提出部门名称,...,位置)
OPEN  cursor1;(打开游标)
FETCH  FROM  cursor1  INTO  v_DEPTNO,  V_DEPTNAME,  v_LOCATION;
DELETE  FROM  DB2ADMIN.ORG  WHERE CURRENT  OF  cursor1;(删除DB2ADMIN.ORG的记录; CURRENT  OF  cursor1这是的游标是指向某一个位置;删除游标指向的当前行。)
CLOSE  cursor1;(关闭游标,也可做一个循环,删除所有的内容)
更新游标对应的数据行
DECLARE  cursor1  CURSOR  FOR   SELECT  DEPTNO,  DEPTNAME,  LOCATION
FROM  DB2ADMIN.ORG FOR  UPDATE;
OPEN  cursor1;
FETCH  FROM  cursor1  INTO  v_DEPTNO,  v_DEPTNAME,  v_LOCATION;
UPDATE  DB2ADMIN.ORG  SET   DEPTNAME  =  ‘NEW NAME’WHERE CURRENT  OF  cursor1;
CLOSE  cursor1;
使用游标返回多个结果集



九 异常处理器

异常处理器的声明

DECLARE handler-type HANDLER FOR condition handler-action; 
(语法结构)(异常处理器是需要预先声明的)
异常处理器的类型(handler-type)
异常处理器的类型有如下几种:
CONTINUE(继续)
在异常处理器操作完成之后,会继续执行产生这个异常语句之后的下一条语句。 
EXIT(记录完后就退出,不再继续执行)
在异常处理器操作完成之后,存储过程会终止,并将控制返回给调用者。 
UNDO(撤销所做的记录,退出整个程序)
在异常处理器操作执行之前,DB2会回滚存储过程中执行的SQL操作。在异常处理器操作完成之后,存储过程会终止,并将控制返回给调用者。 
异常处理器和SQLSTATE
异常处理器可以处理基于特定SQLSTATE值的定制异常,或者处理预定义异常的类。预定义的3种异常如下所示: 
NOT FOUND
标识导致SQLCODE值为+100或者SQLSATE值为02000的异常。这个异常通常在SELECT没有返回行的时候出现。 
SQLEXCEPTION
标识导致SQLCODE值为负的异常。 
SQLWARNING
标识导致警告异常或者导致+100以外的SQLCODE正值的异常。 
注:如果产生了NOT FOUND 或者SQLWARNING异常,并且没有为这个异常定义异常处理器,那么就会忽略这个异常,并且将控制流转向下一个语句。如果产生了SQLEXCEPTION异常,并且没有为这个异常定义异常处理器,那么存储过程就会失败,并且会将控制流返回调用者
异常处理器示例
如下示例声明了两个异常处理器。 EXIT处理器会在出现SQLEXCEPTION 或者SQLWARNING异常的时候被调用。EXIT处理器会在终止SQL程序之前,将名为stmt的变量设为“ABORTED”,并且将控制流返回给调用者。UNDO处理器会将控制流返回给调用者之前,回滚存储过程体中已经完成的SQL操作。 
   DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING 
               SET stmt = 'ABORTED'; 
    DECLARE UNDO HANDLER FOR NOT FOUND; 
异常处理器定制
 如果预定义异常集不能满足需求,就可以为特定的SQLSTATE值声明定制异常,然后再为这个定制异常声明处理器。语法如下: 
   DECLARE unique-name CONDITION FOR SQLSATE 'sqlstate' 
更为复杂的异常处理器示例(1 of 2)
    -- Generic Handler 
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, SQLWARNING, NOT FOUND
    BEGIN NOT ATOMIC
        -- Capture SQLCODE & SQLSTATE 
        SELECT SQLCODE, SQLSTATE 
            INTO hSqlcode, hSqlstate 
            FROM SYSIBM.SYSDUMMY1; 
            
            -- Use the poGenStatus variable to tell the procedure -- what type of error occurred
            CASE hSqlstate 
            WHEN '02000' THEN 
                  SET poGenStatus=5000; 
            WHEN '42724' THEN 
                 SET poGenStatus=3; 
            ELSE IF (hSqlCode < 0) THEN 
              SET poGenStatus=hSqlCode; 
            END IF; 
     END 
  上面的异常处理器会在出现SQLEXCEPTION, SQLWARNING, NOT FOUND异常的时候触发。异常处理器会取出当前的SQLCODE, SQLSTATE,然后根据它们的值来设置输出参数(poGenStatus)的值。 

十 编写和调试存储过程
创建存储过程
将存储过程部署到本地或远程DB2数据库
修改并重新部署存储过程
对存储过程进行测试和Debug
使用Command Editor创建存储过程(1 of 3)





posted @ 2012-12-11 16:34 顺其自然EVO 阅读(2583) | 评论 (0)编辑 收藏

跟屌丝一起学习 DB2 第五课 存储过程(一)

DB2 存储过程

一、什么是存储过程

受 DB2 服务器控制的一段可执行程序

可以通过SQL的CALL语句来完成对存储过程的调用

在存储过程中可以包含业务逻辑

存储过程可以在本地或远程进行调用

存储过程可以接收或传递参数,生成结果集

二、存储过程特征

包含使用sql语句的过程构造

存储在数据库中且在db2 服务器上运行;

可以由正在使用的sql的应用程序根据名称来调用;

允许应用程序分2部分允许,在客户机上运行应用程序,在服务器上运行存储过程

存储过程在应用程序中的优势

减少了客户机与服务器直接的网络使用率

增强了硬件和软件功能

提高了安全性

减少了开发成本并且提高了可靠性

集中处理了公共例程的安全性、管理和维护

通过sql pl 当前的语句集合和语言特性,可以用sql开发综合的、高级的程序

例如函数、存储过程和触发器。这样便可以将业务逻辑封装到易于维护的数据库对象中,从而提高数据库应用程序的性能。

SQL PL 支持本地和全局变量,包括声明和赋值,还支持条件语句和迭代语句、控制语句的转移、错误管理语句以及返回结果集的方法。

三、什么时候使用存储过程

使用存储过程的合适时机:

应用程序的性能无法满足预期时

客户端数量较多且应用程序中SQL代码分散时

应用程序需要进行繁重的数据库操作,同时这些操作并不需要进行太多的客户交互

应用程序代码更改频繁

需要对客户应用代码进行访问控制时

客户应用需要在一次操作中执行多条 SQL 语句



五、数据类型


字符型:char varchar

日期型 date

数字型 number decilmal integer

详细请看屌丝大哥 db2数据类型介绍的那一课

六、Db2 存储过程基本语法

6.1 存储过程结构

CREATE OR REPLACE PROCEDURE <过程名>

( [ IN | OUT | INOUT ]  参数名  数据类型  默认值 )

LANGUAGE  SQL

BEGIN

      业务逻辑代码

END;

IN(输入参数)

只是将实参传递给存储过程,但在存储过程中不能对其进行修改。换句话说,对于存储过程而言它是只读的。

OUT(输出参数)

在存储过程结束时向调用者返回。一般在过程中都会被赋值。

INOUT(输入输出参数)

上述两种参数类型的结合体。它可以帮助调用者将实参传递给进程,另外它也能够作为输出参数被修改和赋值。

复合语句实例

复合语句是指包含在BEGIN和END间的语句。它一般包括如下语句类型:

声明语句

赋值语句

控制语句

条件处理语句




说明:

1. 复合语句可以嵌套使用。

2. BEGIN语句可以和标签组合使用,这样可以更清晰的标识语句块的范围。

6.2 变量声明与变量赋值

变量声明语法:

DECLARE 变量名  数据类型 初始值;

Delcare DiaoSiName varchar(20);

变量赋值语法 :set 变量名=值;

例如:给屌丝姓名变量赋值。

Set DiaoSiName = 奶娃;

变量声明

DECLARE  my_var  INTEGER  DEFAULT  6;

条件声明

DECLARE  not_found  CONDITION  FOR  SQLSTATE  ‘02000’;

游标声明

DECLARE  c1  CURSOR  FOR  select  *  from  staff;

异常处理器声明

DECLARE  EXIT  HANDLER  FOR  SQLEXCEPTION  …;

语法

SET  lv_name  =  expression;

SET  lv_name  =  NULL;

示例

(1) SET  salary  =  salary  +  salary  *  0.1;

(2) SET  init_salary  =  NULL;

(3) SET  salary  =  (select  salary  from  employee  where empno  =  lv_emp_num);

注: 如果 SELECT 语句返回记录超过一行,示例 3 将会返回SQLERROR。






模块 - 规格说明(Module Specification)

模块可以发布type, SP, UDF以供外部使用。
CREATE  OR  REPLACE  MODULE  myMod;
ALTER  MODULE  myMod  PUBLISH
            TYPE  myRowTyp  AS  ANCHOR  ROW  myTab;
ALTER  MODULE  myMod  PUBLISH
            FUNCTION  myFunc(val1  ANCHOR  myTab.col1)
                RETURNS myRowTyp;
ALTER  MODULE  myMod  PUBLISH
            PROCEDURE  myProc(OUT  param1  ANCHOR  myTab.col2);
模块 - 实现(Module Implementation)
下面的代码是模块的实现部分:
ALTER  MODULE  myMod  ADD  VARIABLE  pkgVar  ANCHOR  myTab.col1;
ALTER  MODULE  myMod  ADD  FUNCTION   myFunc(val1  ANCHOR  myTab.col1)  RETURNS  myRowTyp
BEGIN
    DECLARE  var1  myRowTyp;
    SELECT  *  INTO  var1  FROM  myTab  WHERE  col1  <  val1  AND  col1  >  pkgVar;
RETURN  var1;
END
ALTER  MODULE  myMod  ADD  PROCEDURE  myProc(OUT  param1  ANCHOR  myTab.col2)
BEGIN
    DECLARE  varRow  myRowTyp;
SET  param1  =  varRow.col2  –  pkgVar;
END

模块 - 其他语句

删除整个模块
DROP  MODULE  myMod;
保留规格说明内容,删除实现
ALTER  MODULE  myMod  DROP  BODY;
删除模块中的存储过程(SP)
ALTER MODULE myMod DROP PROCEDURE myProc;
将模块的执行权限赋给joe
GRANT EXECUTE ON MODULE myMod TO joe;

七、控制语句
IF语句
格式:
IF  条件1   THEN  statement1;
ELSEIF  条件2  THEN  statement2;
ELSE  statement3;
  END  IF;
注:条件成立时为TRUE (真),不成立时为FALSE(假) 和 NULL
IF语句例子
IF  rating  =  1  THEN
UPDATE  EMPLOYEE  SET  salary  =  salary*1.10
     WHERE  empno  =  i_num;(如果满足于...时,薪水调整1.1倍)
ELSEIF  rating  =  2  THEN
UPDATE  EMPLOYEE  SET  salary  =  salary*1.05
     WHERE  empno  =  i_num;
ELSE
UPDATE  EMPLOYEE  SET  salary  =  salary*1.03
     WHERE  empno  =  i_num;
END  IF;
CASE语句(1 of 2)

简单CASE语句



稍加变形的CASE语句




LOOP语句
语法
[LABEL]  LOOP
    SQL-procedure-statements;
   END  LOOP  [LABEL];
示例
fetch_loop:  LOOP
FETCH  c1  INTO  v_firstname,  v_lastname;
     SET  counter  =  counter  +  1;
     IF  counter  =  51  THEN
          LEAVE  fetch_loop;
     END  IF;
END  LOOP  fetch_loop;
FOR语句
语法
[LABEL]  FOR  for-loop-name  AS  [cursor-name  CURSOR  FOR]
      select-statement  
DO
      SQL-procedure-statements;
   END  FOR  [LABEL];
示例
DECLARE  fullname  CHAR(40);
FOR  v1  AS  c1  CURSOR  FOR  SELECT  firstnme,  midinit,  lastname  FROM  employee
DO
             SET  fullname=lastname||‘,’||firstnme||’,’||midinit;
    INSERT  INTO  tname  VALUE  (fullname);
END  FOR;

posted @ 2012-12-11 15:56 顺其自然EVO 阅读(2036) | 评论 (1)编辑 收藏

跟屌丝大哥学习设计模式---观察者模式

Java 观察者模式的浅析

简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新。
 观察者模式的结构

  观察者(Observer)模式是对象的行为型模式,又叫做发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。

 本模式的类图结构如下:


图1、观察者模式的静态结构可从类图中看清楚。

  在观察者模式里有如下的角色:
 . 抽象主题(Subject)角色:主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。主题提供一个接口可以加上或撤销观察者对象;主题角色又叫做抽象被观察者(Observable)角色; 

图2、抽象主题角色,有时又叫做抽象被观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。

  . 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到通知时更新自己; 



图3、抽象观察者角色,可以用一个抽象类或者一个接口实现;在具体的情况下也不排除使用具体类实现。

  . 具体主题(ConcreteSubject)角色:保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;


图4、具体主题角色,通常用一个具体子类实现。

具体观察者(ConcreteObserver)角色:保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口,以便使本身的状态与主题的状态自恰。 


图5、具体观察者角色,通常用一个具体子类实现。

下面给出一个示意性实现的Java代码。首先在这个示意性的实现里,用一个Java接口实现抽象主题角色,这就是下面的Subject接口:


public interface Subject
{
public void attach(Observer observer);

public void detach(Observer observer);

void notifyObservers();
}
代码清单1、Subject接口的源代码。 

  这个抽象主题接口规定出三个子类必须实现的操作,即 attach() 用来增加一个观察者对象;detach() 用来删除一个观察者对象;和notifyObservers() 用来通知各个观察者刷新它们自己。抽象主题角色实际上要求子类保持一个以所有的观察者对象为元素的列表。

  具体主题则是实现了抽象主题Subject接口的一个具体类,它给出了以上的三个操作的具体实现。从下面的源代码可以看出,这里给出的Java实现使用了一个Java向量来保存所有的观察者对象,而 attach() 和 detach() 操作则是对此向量的元素增减操作。


import java.util.Vector;
import java.util.Enumeration;

public class ConcreteSubject implements Subject
{
public void attach(Observer observer)
{
observersVector.addElement(observer);
}

public void detach(Observer observer)
{
observersVector.removeElement(observer);
}

public void notifyObservers()
{
Enumeration enumeration = observers();
while (enumeration.hasMoreElements())
{
((Observer)enumeration.nextElement()).update();
}
}

public Enumeration observers()
{
return ((Vector) observersVector.clone()).elements();
}
private Vector observersVector = new java.util.Vector();
}
代码清单2、ConcreteSubject类的源代码。 
  抽象观察者角色的实现实际上是最为简单的一个,它是一个Java接口,只声明了一个方法,即update()。这个方法被子类实现后,一被调用便刷新自己。

public interface Observer
{
void update();
}
代码清单3、Observer接口的源代码。 

  具体观察者角色的实现其实只涉及update()方法的实现。这个方法怎么实现与应用密切相关,因此本类只给出一个框架。
public class ConcreteObserver implements Observer
{
public void update()
{
// Write your code here
}
}
代码清单4、ConcreteObserver类的源代码。 

  虽然观察者模式的实现方法可以有设计师自己确定,但是因为从AWT1.1开始视窗系统的事件模型采用观察者模式,因此观察者模式在Java语言里的地位较为重要。正因为这个原因,Java语言给出了它自己对观察者模式的支持。因此,本文建议读者在自己的系统中应用观察者模式时,不妨利用Java语言所提供的支持。
  Java语言提供的对观察者模式的支持

  在Java语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成Java语言对观察者模式的支持。

  Observer接口

  这个接口只定义了一个方法,update()。当被观察者对象的状态发生变化时,这个方法就会被调用。这个方法的实现应当调用每一个被观察者对象的notifyObservers()方法,从而通知所有的观察对象。


图6、java.util提供的Observer接口的类图。


package java.util;

public interface Observer
{
/**
* 当被观察的对象发生变化时,这个方法会被调用。
*/
void update(Observable o, Object arg);
}
代码清单5、java.util.Observer接口的源代码。 

  Observable类

  被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一个方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。

  java.util.Observable类还有其它的一些重要的方法。比如,观察者对象可以调用java.util.Observable类的addObserver()方法,将对象一个一个加入到一个列表上。当有变化时,这个列表可以告诉notifyObservers()方法那些观察者对象需要通知。由于这个列表是私有的,因此java.util.Observable的子对象并不知道观察者对象一直在观察着它们。


图7、Java语言提供的被观察者的类图。

  被观察者类Observable的源代码:

package java.util;
public class Observable
{
private boolean changed = false;
private Vector obs;

/** 用0个观察者构造一个被观察者。**/

public Observable()
{
obs = new Vector();
}


/**
* 将一个观察者加到观察者列表上面。
*/

public synchronized void addObserver(Observer o)
{
if (!obs.contains(o))
{
obs.addElement(o);
}

}


/**
* 将一个观察者对象从观察者列表上删除。
*/

public synchronized void deleteObserver(Observer o)
{
obs.removeElement(o);
}


/**
* 相当于 notifyObservers(null)
*/

public void notifyObservers()
{
notifyObservers(null);
}


/**
* 如果本对象有变化(那时hasChanged 方法会返回true)
* 调用本方法通知所有登记在案的观察者,即调用它们的update()方法,
* 传入this和arg作为参量。
*/

public void notifyObservers(Object arg)
{
/**
* 临时存放当前的观察者的状态。参见备忘录模式。
*/

Object[] arrLocal;

synchronized (this)
{
if (!changed) return;
arrLocal = obs.toArray();
clearChanged();
}


for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}


/**
* 将观察者列表清空
*/

public synchronized void deleteObservers()
{
obs.removeAllElements();
}


/**
* 将“已变化”设为true
*/

protected synchronized void setChanged()
{
changed = true;
}


/**
* 将“已变化”重置为false
*/

protected synchronized void clearChanged()
{
changed = false;
}


/**
* 探测本对象是否已变化
*/

public synchronized boolean hasChanged()
{
return changed;
}


/**
* 返还被观察对象(即此对象)的观察者总数。
*/

public synchronized int countObservers()
{
return obs.size();
}

}


代码清单6、java.util.Observer接口的源代码。 

  这个Observable类代表一个被观察者对象。一个被观察者对象可以有数个观察者对象,一个观察者可以是一个实现Observer接口的对象。在被观察者对象发生变化时,它会调用Observable的notifyObservers方法,此方法调用所有的具体观察者的update()方法,从而使所有的观察者都被通知更新自己。见下面的类图:


图8、使用Java语言提供的对观察者模式的支持。

  发通知的次序在这里没有指明。Observerable类所提供的缺省实现会按照Observers对象被登记的次序通知它们,但是Observerable类的子类可以改掉这一次序。子类并可以在单独的线程里通知观察者对象;或者在一个公用的线程里按照次序执行。 

  当一个可观察者对象刚刚创立时,它的观察者集合是空的。两个观察者对象在它们的equals()方法返回true时,被认为是两个相等的对象。 
  怎样使用Java对观察者模式的支持

  为了说明怎样使用Java所提供的对观察者模式的支持,本节给出一个非常简单的例子。在这个例子里,被观察对象叫做Watched,也就是被监视者;而观察者对象叫做Watcher。Watched对象继承自java.util.Obsevable类;而Watcher对象实现了java.util.Observer接口。另外有一个对象Tester,扮演客户端的角色。 

  这个简单的系统的结构如下图所示。 


图9、一个使用Observer接口和Observable类的例子。

  在客户端改变Watched对象的内部状态时,Watched就会通知Watcher采取必要的行动。 


package com.javapatterns.observer.watching;

import java.util.Observer;

public class Tester
{
static private Watched watched;
static private Observer watcher;

public static void main(String[] args)
{
watched = new Watched();

watcher = new Watcher(watched);

watched.changeData("In C, we create bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Visual Basic, we visualize bugs."); 
}

}


  代码清单7、Tester类的源代码。 


 代码清单7、Tester类的源代码。 


package com.javapatterns.observer.watching;

import java.util.Observable;

public class Watched extends Observable
{
private String data = "";

public String retrieveData()
{
return data;
}


public void changeData(String data)
{
if ( !this.data.equals( data) )
{
this.data = data;
setChanged();
}


notifyObservers();
}

}


代码清单9、Watcher类的源代码。 

  可以看出,虽然客户端将Watched对象的内部状态赋值了四次,但是值的改变只有三次:

watched.changeData("In C, we create bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Visual Basic, we visualize bugs."); 

  代码清单10、被观察者的内部状态发生了改变。 

  对应地,Watcher对象汇报了三次改变,下面就是运行时间程序打印出的信息:

Data has been changed to: 'In C, we create bugs.'

Data has been changed to: 'In Java, we inherit bugs.'

Data has been changed to: 'In Visual Basic, we visualize bugs.'

  代码清单11、运行的结果。 

 菩萨的守瓶龟

  想当年齐天大圣为解救师傅唐僧,前往南海普陀山请菩萨降伏妖怪红孩儿:“菩萨听说...恨了一声,将手中宝珠净瓶往海心里扑的一掼...只见那海当中,翻波跳浪,钻出个瓶来,原来是一个怪物驮着出来...要知此怪名和姓,兴风作浪恶乌龟。” 

  使用面向对象的语言描述,乌龟便是一个观察者对象,它观察的主题是菩萨。一旦菩萨将净瓶掼到海里,就象征着菩萨作为主题调用了notifyObservers()方法。在西游记中,观察者对象有两个,一个是乌龟,另一个是悟空。悟空的反应在这里暂时不考虑,而乌龟的反应便是将瓶子驮回海岸。 


图10、菩萨和菩萨的守瓶乌龟。
Java中的DEM事件机制

  AWT中的DEM机制

  责任链模式一章中曾谈到,AWT1.0的事件处理的模型是基于责任链的。这种模型不适用于复杂的系统,因此在AWT1.1版本及以后的各个版本中,事件处理模型均为基于观察者模式的委派事件模型(Delegation Event Model或DEM)。 

  在DEM模型里面,主题(Subject)角色负责发布(publish)事件,而观察者角色向特定的主题订阅(subscribe)它所感兴趣的事件。当一个具体主题产生一个事件时,它就会通知所有感兴趣的订阅者。 

  使用这种发布-订阅机制的基本设计目标,是提供一种将发布者与订阅者松散地耦合在一起的联系形式,以及一种能够动态地登记、取消向一个发布者的订阅请求的办法。显然,实现这一构思的技巧,是设计抽象接口,并把抽象层和具体层分开。这在观察者模式里可以清楚地看到。 

  使用DEM的用词,发布者叫做事件源(event source),而订阅者叫做事件聆听者(event listener)。在Java里面,事件由类代表,事件的发布是通过同步地调用成员方法做到的。 

  Servlet技术中的的DEM机制

  AWT中所使用的DEM事件模型实际上被应用到了所有的Java事件机制上。Servlet技术中的事件处理机制同样也是使用的DEM模型。 

  SAX2技术中的DEM机制

  DEM事件模型也被应用到了SAX2的事件处理机制上。 

  观察者模式的效果

  观察者模式的效果有以下的优点: 

  第一、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。 

  由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。 

  第二、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知, 

  观察者模式有下面的缺点: 

  第一、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 

  第二、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。 

  第三、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。 

  第四、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。 

  观察者模式与其它模式的关系

  观察者模式使用了备忘录模式(Memento Pattern)暂时将观察者对象存储在被观察者对象里面。

  问答题

  第一题、我和妹妹跟妈妈说:“妈妈,我和妹妹在院子里玩;饭做好了叫我们一声。”请问这是什么模式?能否给出类图说明?

  问答题答案

  第一题答案、这是观察者模式。我和妹妹让妈妈告诉我们饭做好了,这样我们就可以来吃饭了。换用较为技术化的语言来说,当系统的主题(饭)发生变化时,就告诉系统的其它部份(观察者们,也就是妈妈、我和妹妹),使其可以调整内部状态(有开始吃饭的准备),并采取相应的行动(吃饭)。

  系统的类图说明如下。


图11、系统的类图。


网上商店中的商品在名称、价格发生变化时,必须自动通知会员,Java的API为我们提供了Observer接口和Observable类来实现所谓观察者模式。
Observable(可观察者)类允许在自身发生改变时,通知其它对象(实现接口Observer,观察者)。

下面是一个可观察者(产品类):
import java.util.*;
public class product extends Observable{ 
   private String name;////产品名
   private float price;////价格

   public String getName(){ return name;}
   public void setName(String name){
    this.name=name;
   ////设置变化点 
    setChanged();
    notifyObservers(name);////通知观察者

   }   

   public float getPrice(){ return price;}
   public void setPrice(float price){
    this.price=price;
   ////设置变化点
    setChanged();
    notifyObservers(new Float(price));

   }

   ////以下可以是数据库更新 插入命令.
   public void saveToDb(){
   System.out.println("saveToDb");
    }

}

下面是两个观察者:
import java.util.*;
public class NameObserver implements Observer{

   private String name=null;
   public void update(Observable obj,Object arg){
     if (arg instanceof String){
      name=(String)arg;
      ////产品名称改变值在name中
      System.out.println("NameObserver :name changet to "+name);

     }

      }
   }

import java.util.*;
public class PriceObserver implements Observer{
   private float price=0;
   public void update(Observable obj,Object arg){
     if (arg instanceof Float){

      price=((Float)arg).floatValue();
  
      System.out.println("PriceObserver :price changet to "+price);

     }


   }

}
下面是测试类:
public class Test {

   public static void main(String args[]){
    Product product=new Product();
    NameObserver nameobs=new NameObserver();
    PriceObserver priceobs=new PriceObserver();

    ////加入观察者
    product.addObserver(nameobs);
    product.addObserver(priceobs);

    product.setName("applet");
    product.setPrice(9.22f);

   }
}

 

posted @ 2012-12-11 11:51 顺其自然EVO 阅读(485) | 评论 (0)编辑 收藏

从桌面应用自动化测试看移动应用自动化测试

  自从图形化界面成为个人桌面电脑的主流,应用程序复杂程度与日俱增,针对人机交互的自动化测试迫在眉睫,从而在市场上涌现了一大批针对图形界面应用程序功能测试的自动化测试工具(参考链接1)。2001年QTP第一个版本发布;2002年Robot初始版发布。自此,自动化工具已经经历了十年的发展。随着近两年移动应用呈现爆炸性的增长,移动应用自动化测试工具也开始陆续呈现(参考链接2)。

  需求的延续

  无论从PC端应用的自动化测试,还是移动应用的自动化测试,人们的关注点从未转移,期望也从不改变,那就是,尽可能多的模拟人工测试动作和相应的结果检查,从而释放手工劳动,替代大量重复性的执行和验证工作。进入移动应用时代,移动应用项目开发一直走的是“短小精干”的路子,即应用程序小而精。开发模式也抛弃了传统的规范流程,热衷于敏捷式开发。版本发布周期约来越短,迭代频密。这些似乎与自动化测试遥不可及。但是,随着移动应用逐渐从个人娱乐领域渗透到商业应用,诸如金融、办公、政务等方面的应用比重逐步扩大,对移动应用质量的要求也越来越高,自动化测试始终会回到人们的视线之内。在加上安卓特有的碎片化问题,使得安卓平台自动化回归测试和兼容性测试的呼声极高。

  理念的传承

  回顾桌面应用的自动化测试历程,我们看到,工具的发展经历了从最初“坐标点操作”过渡到“对象识别”的过程。移动应用测试工具走的路子也有几分相似。以开放的Android平台为例,最开始出现Monkey/MonkeyRunner等坐标点操作的工具(后来有很多工具开发商做了对MonkeyRunner的封装);之后出现了如Robotium等基于源码层面对于界面控件识别的工具;也有一些工具开发商如DroidPilot.com推出了纯粹的对象识别工具;当然,也有一些如PerfectoMobile.com的工具开发商,为了兼容iOS/BlackBerry/Windows Phone等平台,采用图像识别技术。但无论如何,“关键字驱动”、“数据驱动”等理念已经是传统PC行业自动化测试的成功经验,移动应用测试方面应该借鉴。再搭配性能测试工具、轻量级测试需求管理、用例管理、缺陷跟踪等工具,相信足以成为移动应用项目质量保证的基础工具支撑。

  有所不能vs凡事都能

  似乎所有管理者都期望一旦引入自动化测试,则万事大吉,貌似自动化能做到全方位的测试服务,可以释放测试工程师了。但事实求是的说,即使在拥有十年历程的传统自动化测试行业,自动化所能涉及的测试用例比例也是有限,通常覆盖60%~80%的测试用例,已经能说是不错的成绩了。问题是,项目的成本和进度,以及测试人员的配备,是否能足以支撑自动化测试持续的进行。否则事半功倍,未免太可惜了。借鉴传统项目的自动化测试失败案例,对于项目预算相对较少的移动应用开发项目,考虑引入自动化测试的确需要慎之又慎。

  精益求精

  然而对于自动化测试工程师来说,通常并不满足于部分用例的自动化测试,甚至仅仅是自动化冒烟测试。他们总想走的更远,甚至不惜代价去完善一些凤毛麟角之功能。当然,从这一点也可以看出自动化测试工程师们精益求精的精神,同时,也对自动化测试工具开发者提出了更高的要求。从目前发展现状来看,他们也的确在着眼于提高工具的测试深度和广度,增强工具易用性,剥离工具对于源代码的依赖,延伸传统自动化测试的方法论。希望看到移动应用自动化测试领域呈现蓬勃的发展。

  参考链接

  1、<List of GUI testing tools - wikipedia>
  http://en.wikipedia.org/wiki/List_of_GUI_testing_tools

  2、<安卓应用自动化测试工具大汇总–测试窝>
  http://www.testwo.com/space.php?uid=11328&do=blog&id=5956

版权声明:本文出自 anthony.wang 的51Testing软件测试博客:http://www.51testing.com/?507238

原创作品,转载时请务必以超链接形式标明本文原始出处、作者信息和本声明,否则将追究法律责任。

posted @ 2012-12-11 09:40 顺其自然EVO 阅读(595) | 评论 (0)编辑 收藏

跟屌丝大哥学设计模式-单例模式

概念:
  java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
哈哈 我们可以这样分 屌丝单例模式:懒汉式屌丝单例,饿汉式屌丝单例,登记式屌丝单例。

  单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

首先看一个经典的单例实现。

public class Singleton {

    private static Singleton uniqueInstance = null;

 

    private Singleton() {

       // Exists only to defeat instantiation.

    }

 

    public static Singleton getInstance() {

       if (uniqueInstance == null) {

           uniqueInstance = new Singleton();

       }

       return uniqueInstance;

    }

    // Other methods...

}

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

但是以上实现没有考虑线程安全问题。所谓线程安全是指:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。

public class TestDiaoSiStream {
     private String DiaoSiname;
     public String getDiaoSiname() {
         return DiaoSiname;
     }
     public void setDiaoSiname(String DiaoSiname) {
         this.DiaoSiname = DiaoSiname;
     } 
     //该类只能有一个实例
     private TestDiaoSiStream(){}    //私有无参构造方法
     //该类必须自行创建
     //有2种方式
     /*private static final TestDiaoSiStream ds=new TestDiaoSiStream();*/
     private static TestDiaoSiStream ds1=null;
     //这个类必须自动向整个系统提供这个实例对象
     public static TestDiaoSiStream getTestDiaoSiTest(){
         if(ds1==null){
             ds1=new TestDiaoSiStream();
         }
         return ds1;
     }
     public void getInfo(){
         System.out.println("output message "+DiaoSiname);
     }
 }

public class TestMain {
     public static void main(String [] args){
         TestDiaoSiStream  ds=TestDiaoSiStream.getTestDiaoSiTest();
         ds.setDiaoSiname("奶娃");
         System.out.println(ds.getDiaoSiname());
         TestDiaoSiStream ds1=TestDiaoSiStream.getTestDiaoSiTest();
         ds1.setDiaoSiname("奶娃");
         System.out.println(ds1.getDiaoSiname());
         ds.getInfo();
         ds1.getInfo();
         if(ds==ds1){
             System.out.println("创建的是同一个屌丝实例");
         }else if(ds!=ds1){
             System.out.println("创建的不是同一个屌丝实例");
         }else{
             System.out.println("application error");
         }
     }
 }

运行结果:
  奶娃
  奶娃
  output message 奶娃
  output message 奶娃
  创建的是同一个屌丝
实例

结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象

1.饿汉式单例类

//饿汉式单例类.在类初始化时,已经自行实例化 
 public class Singleton1 {
     //私有的默认构造子
     private Singleton1() {}
     //已经自行实例化 
     private static final Singleton1 single = new Singleton1();
     //静态工厂方法 
     public static Singleton1 getInstance() {
         return single;
     }
 }

2.懒汉式单例类
//懒汉式单例类.在第一次调用的时候实例化 
 public class Singleton2 {
     //私有的默认构造子
     private Singleton2() {}
     //注意,这里没有final    
     private static Singleton2 single=null;
     //静态工厂方法 
     public synchronized  static Singleton2 getInstance() {
          if (single == null) {  
              single = new Singleton2();
          }  
         return single;
     }
 }
3.登记式单例类

import java.util.HashMap;
 import java.util.Map;
 //登记式单例类.
 //类似Spring里面的方法,将类名注册,下次从里面直接获取。
 public class Singleton3 {
     private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
     static{
         Singleton3 single = new Singleton3();
         map.put(single.getClass().getName(), single);
     }
     //保护的默认构造子
     protected Singleton3(){}
     //静态工厂方法,返还此类惟一的实例
     public static Singleton3 getInstance(String name) {
         if(name == null) {
             name = Singleton3.class.getName();
             System.out.println("name == null"+"--->name="+name);
         }
         if(map.get(name) == null) {
             try {
                 map.put(name, (Singleton3) Class.forName(name).newInstance());
             } catch (InstantiationException e) {
                 e.printStackTrace();
             } catch (IllegalAccessException e) {
                 e.printStackTrace();
             } catch (ClassNotFoundException e) {
                 e.printStackTrace();
             }
         }
         return map.get(name);
     }
     //一个示意性的商业方法
     public String about() {    
         return "Hello, I am RegSingleton.";    
     }    
     public static void main(String[] args) {
         Singleton3 single3 = Singleton3.getInstance(null);
         System.out.println(single3.about());
     }
 }

posted @ 2012-12-07 14:55 顺其自然EVO 阅读(458) | 评论 (0)编辑 收藏

软件测试过程中对bug的处理流程

 又属于一篇普及文,希望自己在被各种技术吸引的同时,能时常来整理和总结软件测试最基本的知识。

  从刚工作时接触的第一个缺陷管理工具禅道,到redmine、JIRA、bugzilla ,再到现在的QC,当然还有其它种的开源的或商业的缺陷管理工具,它们的本质是一样的,就是来管理缺陷的生命周期。

  其实,你理解任意的一款工具,其它的工具也一定能无师自通。这不谈某款工具,单把它本质的一些东西抽离出来与大家分享。

  Bug的属性

  Bug重现环境

  这个应该是我们重现bug的一个前提,如果没有这个前提,我们可能会无法重现问题,或者跟本就无从下手。

  操作系统

  这个是一般软件运行的一大前提,基本上所有的软件都依赖于操作系统之上的,对于一个软件来说,要想在某个操作系统上运行,必须要对这个操作系统支持,这就需要有真对性的设计与开发。对于不同的操作系统,其可能存在差异(如:win xp 与 win 7)或本质的区别(如 win 7 与 CentOS linux ),所以,操作系统环境是重现问题的一个重要前提。

  浏览器

  对于B/S系统,或面向大众的互联网产品(网站,邮箱等),浏览器的兼容性也是必须测试的一个重点,对于现在的浏览器市场,各式的浏览器都有其用户群,要想使产品大众化,必须考虑这些产品的兼容性问题。

  不同的浏览器之间(IE、 firefox、chrome、opera 等),甚至同一系列不同版本(ie6/ie7/ie8/ie9等)都可能存在兼容性问题,所以,对于这类应用,浏览器环境重现bug前提条件之一。

  其它(这个“其它”非常重要)

  对于不同的系统发现重现问题,都会有其特定的前提,拿我测试的邮箱来说,必须要描述其是在测试线还是现网环境,而且还要附带一重现问题的帐号等。

  对于c/s软件,可能还要考虑与其它常用软的兼容等,例如,是在安装的某款软件后,对本软件的安装和使用造成影响。这些都是重现问题的必须描述的环境。

  问题类型

  根据JIRA的管理系统的划分,bug 只是问题的一种,它可以用于跟踪多种不同类型的问题(其实,他只是将bug做为一子类而已)。

  JIRA系统缺省提供的问题类型(大部分的系统都可以自定义类型的,这样就增加了灵活性。)

  ● Bug : 测试过程、维护过程发现影响系统运行的缺陷。(这就是一般测试人员所提交的bug)

  ● New Feature :  对系统提出的新功能。(单个的小需求可以,如果大的话,就相当于一个需求,放到这里是不合理的。)

  ● Task : 需要完成的一任务。(开发或测试任务指派。)

  ● Improvement : 对现有系统功能的改进。(一般产品经理或产品体验师做的事)

  当然,不同的公司,他们的人员定位与职责是不太相同的,按照上面的分类,JIRA就不是简单的缺陷管理系统了,它涵盖一项目(或产品)所需要处理的任务、需求与缺陷。

 Bug 类型:

  这里缩小范围,单指我们测试人员在测试过程中发现的缺陷,发现产品缺陷其实就是测试人员工作的主要目的。当然,你要确定一个问题的类型,也需要对项目(或产品)有比较深的理解。是代码缺陷还是设计缺陷有时候就不太容易区分,当然,这个划分,对于开发定位问题影响很小,但对于问题类型的统计就比较重要了。

  下面看一些常见的分类:

  划分方式一:

  代码错误

  设计缺陷

  界面优化

  配置相关

  安装部署

  性能问题

  标准规范

  测试代码

  其它

  划分方式二:

  功能类(function)

  性能类(performance)

  界面类(UI)

  易用性类(usability)

  兼容性类(compatibility)

  其它(else)

  这个分类当然是可以自定义的,具我接触的缺陷管理都是可以自定义的,既然是对问题的管理,那么你当然可以拿来做特定环境下的系统来使用,或我就想用这个系统来指派任务,那么我的自定义类型为前端任务、后端任务、测试任务、配置部署...

  缺陷等级

  缺陷等级,这个划分也比较灵活,有分三级或四级,也有分五级的。

  致命

  一招毙命的缺陷,使你的系统无法运行,有造成数据泄漏的安全性问题。

  严重

  可以引起易于纠正的异常情况、可能引起易于修复的故障或对产品外观难以接受的缺陷。

  一般

  指不影响产品的运转和运行、不会成为故障起因,但对产品外观和下道工序影响较大的缺陷

  轻微

  轻微缺陷是指对产品外观和下道工序可能会有轻微影响的缺陷

  建议

  增加用户使用体验的建议性问题。(一般情况下,建议也为做为缺陷的一种。这个跟系统的类型与需求有关)

  缺陷优先级(priority)

  当问题处理人员在面对许多问题需要处理进,就需要问题进行优先级排序。我们做事情的安排,操作系统有处理进程等都在使用着优先级。

  优先级的划分:

  低——>中——>高——>紧急

  延迟处理——>正常排队——>优先处理——>紧急处理

  Bug 的严重程度和优先级是含义不同但相互联系密切的两个概念,它们从不同的侧面描述了软件缺陷对软件质量和最终用户的影响程序和处理方式。

  一般地,严重程序高的软件缺陷具有较高的优先级。严重程度高说明缺陷对软件造成的危害性大,需要优先处理,而来严重程序低的缺陷可能只是软件不太尽善尽美,可以稍后处理。

  严重程度高优先级不一定高:

  如果某个严重的软件缺陷只在非常极端的条件下产生,则没有必要马上处理。

  如果某一个软件缺陷,需要重新修改软件的整体架构,可能会产生更多的潜在缺陷,而且软件由于市场的压力必须尽快发布,此时即使缺陷的严重性很高,是否需要修正,需要全盘考虑。

  严重程度优先级不一定低

  如果是软件名称或公司名称的拼写错误,虽然说其属于界面错误,严重程度不高,但其关系到软件和公司的市场开解,必须尽快修正。

  缺陷状态

  对于一个问题,其处理过程是一个周期,周期的不同阶段,其所处的状态也是不一样的。不同状态所对应的处理人也是不一样的。

  打开:表示问题被提交等待有人处理。

  重新指派:问题被重新指派给某人处理。

  处理:问题在处理中,尚未完成。

  固定:确认此问题存在,但暂时不进行处理。

  回归:对已经修复的问题进行回归确认。Reopened :

  关闭:问题的最后一个状态。

  Bug处理流程

  下面通过一个比较完整的bug的处理流程图,更深刻的理解bug的状态以一个bug的生命周期。

  提交(打开)缺陷

  在提交一个缺陷的缺陷,首先尽量描述这个缺陷的属性。Bug重现环境,bug类型,bug等级,bug的优先级以及详细的重现步骤,结果与期望等。

  当然,我们在提交一个问题之前首先应该保证,这个缺陷是没有被提过的,以免造成重复缺陷单。

  如果是回归不通过的缺陷,其状态又会变为打开状态。

 分配(转交)缺陷

  这一步不是必须的,跟项目模式有关,有些公司测试部门与开发部门独立,那么测试人员就不确定自己测试的模块是由哪位开发人员负责的,在这种情况下,测试人员统一把问题指派给项目组长或经理,由项目组长(或经理)对问题进行确认后再次分配给相应的开发人员。

  有些测试人员是穿插到不同研发团队中的,所以对不同的开人发员负责的开发模块非常清楚,这个时候就可以将问题直接指派给相应的开发人员。

  也有一种情况,本来此问题应该由A开发人员负责,但由于A开发人员的调离或辞职,些问题为转交给其它人员处理。“分配”强调是上级对下级;“转交”强调的是平级之间。

  确认缺陷

  当开发人员接到一个缺陷时,首先是对其进行分析与重现,如果对其进行分析发现不是缺陷(可能由于测试人员不了解需求)或无法对此问题进行重现,那么就需要将此问题反回给测试人员,并注明原因。如果确认为缺陷则需要对其进行处理。

  推迟处理

  在处理问题之后,还需要进行一次判断,是否需要推迟处理,有些需求已经确认了是问题,由于其可能在极端情况下才会出现,或需要对系统架构进行改动,或其优先级非常低,所以暂时不需要对此问题进行处理(或到下个版本进再进行修复)。

  固定

  对于推迟处理的问题可以暂时进行固定(“固定”为QC中的叫法。)一般固定的问题需要经过项目经理与测试经理协商后才能固定。

  处理缺陷

  开发人员在确认完一个问题需要处理时,那么就对其进行处理工作。(例如,redmine 是支持处理人时时更新问题处理进度的,如 已处理30% ,已处理80% 等,当然,对于短时间内可以修复的问题就没必要时时的去更新处理进度。)

  回归缺陷

  回归缺陷对于测试人员来说是非常重要的工作,其有三个入口两个出口。

  确认非缺陷问题:对于提交的一个缺陷,开人员处理为非问题或无法重现,然后直接转交给测试人员回归。测试人员再次确认,如果真如开发人员所说,则将问题关闭。如果非开发人员所说,是由于问题描述模糊或其它原因喂重现问题,则再次注明原因转给开发人员。

  确认修复问题:对开发人员修复的问题再次进行确认,确认能过,则关闭问题。确认不通过,将问题再次打开并转给开发人员。

  确认固定问题:有计划的对固定问题进行确认,有些固定问题随着时间的推移,版本的更新或已经不存在了,对这类问题应该及时关闭。有些固定问题依然存在且变得紧急,对于这类问题应该及时打开交给开发人员处理。

  关闭缺陷

  对于已经修复的缺陷进行关闭,这也是一个缺陷的最后一个状态。

  --------------------------------------------------------------------------------

  注1:文中提到了产品与项目,好多人分不清项目与产品,各自有各自的理解。我个人从用户群上来划分。如果面向的是特定客户的需求,那么称其为项目,如某医院的医疗系统,某公司的管理系统。面向大众用户且长期运营的需求,称为产品,如,某网站,某网络游戏。

  如果小A让我给他做一个网站呢?对于我来说,小A是我的客户,这个网站对我来说就是一个项目,对于小A来说,他的网站是面向大众用户的,那么对于小A来说,网站就是自己的产品。

  富士康带工苹果手机是一样的道理,富士康接到苹果的订单,那么对富士康来说是个项目,完成项目,拿到钱就算项目结束。苹果手机对苹果公司来说是一个产品,它长期持有这个产品的所有权,并且不段的更新自己的产品。

  注2:本文中用到了 bug、缺陷、问题等三个词语,用词比较模糊,本文中表示为一个事物。

 分配(转交)缺陷

  这一步不是必须的,跟项目模式有关,有些公司测试部门与开发部门独立,那么测试人员就不确定自己测试的模块是由哪位开发人员负责的,在这种情况下,测试人员统一把问题指派给项目组长或经理,由项目组长(或经理)对问题进行确认后再次分配给相应的开发人员。

  有些测试人员是穿插到不同研发团队中的,所以对不同的开人发员负责的开发模块非常清楚,这个时候就可以将问题直接指派给相应的开发人员。

  也有一种情况,本来此问题应该由A开发人员负责,但由于A开发人员的调离或辞职,些问题为转交给其它人员处理。“分配”强调是上级对下级;“转交”强调的是平级之间。

  确认缺陷

  当开发人员接到一个缺陷时,首先是对其进行分析与重现,如果对其进行分析发现不是缺陷(可能由于测试人员不了解需求)或无法对此问题进行重现,那么就需要将此问题反回给测试人员,并注明原因。如果确认为缺陷则需要对其进行处理。

  推迟处理

  在处理问题之后,还需要进行一次判断,是否需要推迟处理,有些需求已经确认了是问题,由于其可能在极端情况下才会出现,或需要对系统架构进行改动,或其优先级非常低,所以暂时不需要对此问题进行处理(或到下个版本进再进行修复)。

  固定

  对于推迟处理的问题可以暂时进行固定(“固定”为QC中的叫法。)一般固定的问题需要经过项目经理与测试经理协商后才能固定。

  处理缺陷

  开发人员在确认完一个问题需要处理时,那么就对其进行处理工作。(例如,redmine 是支持处理人时时更新问题处理进度的,如 已处理30% ,已处理80% 等,当然,对于短时间内可以修复的问题就没必要时时的去更新处理进度。)

  回归缺陷

  回归缺陷对于测试人员来说是非常重要的工作,其有三个入口两个出口。

  确认非缺陷问题:对于提交的一个缺陷,开人员处理为非问题或无法重现,然后直接转交给测试人员回归。测试人员再次确认,如果真如开发人员所说,则将问题关闭。如果非开发人员所说,是由于问题描述模糊或其它原因喂重现问题,则再次注明原因转给开发人员。

  确认修复问题:对开发人员修复的问题再次进行确认,确认能过,则关闭问题。确认不通过,将问题再次打开并转给开发人员。

  确认固定问题:有计划的对固定问题进行确认,有些固定问题随着时间的推移,版本的更新或已经不存在了,对这类问题应该及时关闭。有些固定问题依然存在且变得紧急,对于这类问题应该及时打开交给开发人员处理。

  关闭缺陷

  对于已经修复的缺陷进行关闭,这也是一个缺陷的最后一个状态。

  --------------------------------------------------------------------------------

  注1:文中提到了产品与项目,好多人分不清项目与产品,各自有各自的理解。我个人从用户群上来划分。如果面向的是特定客户的需求,那么称其为项目,如某医院的医疗系统,某公司的管理系统。面向大众用户且长期运营的需求,称为产品,如,某网站,某网络游戏。

  如果小A让我给他做一个网站呢?对于我来说,小A是我的客户,这个网站对我来说就是一个项目,对于小A来说,他的网站是面向大众用户的,那么对于小A来说,网站就是自己的产品。

  富士康带工苹果手机是一样的道理,富士康接到苹果的订单,那么对富士康来说是个项目,完成项目,拿到钱就算项目结束。苹果手机对苹果公司来说是一个产品,它长期持有这个产品的所有权,并且不段的更新自己的产品。

  注2:本文中用到了 bug、缺陷、问题等三个词语,用词比较模糊,本文中表示为一个事物。

posted @ 2012-12-07 10:30 顺其自然EVO 阅读(2128) | 评论 (0)编辑 收藏

跟屌丝大哥学DB2-第四课 数据类型 ,表 ,视图,索引,模式,约束(二) (12-6 23:17)

     摘要: 还可以修改表中特定列的特征: ·         列的标识属性 ·         字符串列的长度 ·         列的...  阅读全文

posted @ 2012-12-06 23:25 顺其自然EVO 阅读(690) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 278 279 280 281 282 283 284 285 286 下一页 Last 
<2025年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜