发布新日志

  • 【转】测试职业发展道路[2]

    2009-06-06 13:50:00

    进入技术路线的高级域,根据中级域的四个路线,可以细分成五个路线,分别是资深自动化测试工程师、资深白盒测试工程师、资深性能测试工程师、安全性测试工程师、标准化工程师,这些高级技术类人才完全与常规测试经理平齐,属于软件测试职业发展高级域。
      资深自动化测试工程师由自动化测试工程师晋升而来。如果说常规自动化测试工程师只是负责自动化测试脚本本身的设计与开发,那么资深自动化测试工程师的工作内容就是自动化测试这项工作的实施!笔者早年在IBM公开讲座时候,讲过一篇《以RUP原则实施自动化测试》的主题,RUP里提倡自动化测试是一个庞大的系统工程,绝对不是有了技术、有了工具、有了掌握技术和使用工具的人就可以实施的,而是应该把自动化测试当成一个针对企业自身的项目来看待,需要经过引入、计划、设计、测试、执行、配置管理等环节(参加sincky的blog“天行健-君子以自强不息”),而这些自动化测试的流程搭建,就是资深自动化测试工程师的份内之事。另外,笔者要强调,按照国内外自动化测试领域的发展趋势,我们把自动化测试划分为四个发展阶段(我的blog里也有阐述);也就是说,录制脚本-添加验证点-回放脚本只是最初始的自动化阶段,要在企业实施自动化测试,要有资深自动化测试工程师来设计数据驱动,开发测试框架,甚至一些企业内部自主开发小型测试工具(而非商业工具)的先例,这些也都是建立在资深自动化测试工程师具有深厚的技术底蕴后,主导其他人员协调完成的事情。
      资深白盒测试工程师,其工作内容包含常规白盒测试工程师的内容,除此之外,要协助测试经理或测试总监攻关测试方法与技术性难题,因此其技术水平更加雄厚。如果常规白盒测试工程师是停留在某种程序设计语言类型的代码级测试,那么资深白盒测试工程师就要脱离程序设计语言本身,结合不同架构、多种开发技术交互的情况下,寻找代码测试方法,并具有对代码优化的能力。由于该路线在国内很少有实例参考,这里不再赘述。
      资深性能测试工程师,来源于常规性能测试工程师,按照常规性能测试工程师的技术要求,资深性能测试工程师应该具备性能测试整体方案的设计能力,以及软件系统性能问题定位和性能优化的能力!初此之外,也要对主流的软件开发模式下的应用系统具有敏锐的洞察意识和感知意识。软件开发的架构会日益复杂化,软件应用的各种软硬件平台、数据库类型、服务器类型、网络协议层出不穷,不得不说,都为性能测试的从业者们提出了严峻的考验!值得庆幸的是,各种同类产品的厂商在开发产品时都遵从业内统一标准,性能测试人员结合自身的丰富经验,加上对软件性能测试技术的研究,这样的考验我们欣然面对,这样的人才则会日益增多,在软件测试行业里充当佼佼者地位。
      安全性测试工程师,笔者将其从性能测试工程师衍生出来,因为只有具备性能测试经验的人,才对软件的开发模式、实现架构和技术本身充分了解,才会感知和预见软件系统存在的安全漏洞,加上其本人是测试出身,才知道如何通过系统漏洞尝试攻击软件系统,达到测试的目的。目前国内软件行业对于安全性测试的认识尚未清晰,该职业也更没有普及,一般只限于军事类、机密类、防病毒类或其他高安全性软件的测试工作中。
      再次强调,人类进入文明社会后,任何社会活动都不是独立的个体能够实现的;在高度讲究团队合作、协同办公的今天,软件测试工作更不是测试工程师几个人就能做完所有的事情的;上述各发展路线的技能要求,只是为了增强个人职业突破的砝码,你的砝码越多,“被利用”价值越大,为企业创造利润的程度越高,企业自然给予你更丰厚的回馈!达尔文伯伯的“优胜劣汰”自然规律不会变,“多劳多得、少劳少得”的市场规律也不会变!
      曾经有如此众多的测试职业发展路线放在我面前,结果我没有珍惜;等到软件测试行业发展到成熟阶段,我想入行却入不了行的时候,我才后悔莫及;尘世间干测试最大的不幸莫过于此;如果非要问sincky:再往上的发展通道是什么,那么sincky一定要告诉你,技术专家域!
      在技术路线,向上继续提升的方向,我们称之为“技术专家”;如果说前面描述的技术职位的所涉范围都定位在企业内部,即企业级资深性能测试工程师,那么技术专家,我们可以看作是领域级专项人才!随着软件测试行业的职位不断细化,每个人在自己擅长的领域走向深入,都可以成为该领域的技术专家,技术专家在自已经营的领域里,具有个人独到的见解和深厚的技术实力,而这类人才可以不再从事具体的测试工作,而是提供行业性测试技术咨询、培训等,为软件测试整体行业的发展,起到了鲜明的带头作用。在一些专业的咨询、培训公司,或者IBM、Microsoft等巨型公司,不乏这样的人才;然而目前在我国,这样的人才较少,但是却可以为我们大家提供努力方向,只要我们每个在技术路线供职的测试从业者,规划好自己的职业人生,并以坚韧的毅力和顽强的斗志,若干年后,你我皆可笑谈测试人生,把酒临风,其喜洋洋者矣!而目前在国内几个IT行业发达的省市,专项于软件测试服务或一些大型软件企业,也有这样的职位暂露头角,我们深信,社会对高端人才的需求趋势是越来越大的,更多的优秀企业也会为员工提供更多、更广的发展空间,值此大好形势,就看我们个人如何充分利用这些上升通道了。
      在我们的软件测试从业人员里,有这样一部分群体:他们非计算机相关专业毕业,不懂软件开发,由于国内种种对软件测试人才的偏激认识,认为测试人员不需要懂开发,只要会编写文档、执行用例即可;因此很多测试工程师并不具备开发背景,并且对软件技术掌握肤浅,而对于没有技术底蕴的人强迫其走技术路线,不能不说是一种折磨!因此,这个群体里的朋友,是不是认为自己只能做一辈子常规测试工程师呢?答案是否定的,因为在“双V模型”的左侧,是软件测试职业发展的管理路线。软件测试的管理路线,与通用职业发展示意图的“高管理-低技能”并不完全相同,只因软件测试独具的行业特点,我们认为软件测试行业的非技术路线发展方向,更多的是从软件测试行业衍生出来的职位,如质量保证、配置管理。如果说软件测试职业发展的技术路线更侧重于职业技能的提升,那么这条管理路线则更侧重于职业素质的积累(笔者强调是“侧重”,并不表示不需要);换句话说,技术路线更侧重人的智力因素,而管理路线更侧重人的非智力因素。
      从事了1到3年左右的常规测试工程师,在经过对个人性格特点剖析后,如果认为自己是一个倾向于“高管理-低技能”的类型,那么想要实现自己的职业提升,可以向中级发展域的配置管理工程师、质量保证工程师、业务测试工程师转型。
      配置管理(SCM)与质量保证(SQA)同是CMM中的关键过程域(KPA),也同是现代软件工程里的必要角色,与软件测试同属软件开发团队的重要组成部分。只因这两个角色在软件工程里的人员配比数量相对较少,还不如软件测试这样规模化乃至于形成行业,而最多是一个职业;另外一个社会现象是,企业很少直接从社会直接招聘配置管理工程师和质量保证工程师,而通常的做法是从企业内部的现有测试员工队伍里选拔,而转型后的测试工程师,就成为SCM或SQA。分析其原因,我们可以感知,SCM、SQA与软件测试工程师都是关注于软件质量的相似职位,社会对于配置管理、质量保证的定义和工作内容并未普及,与其直接从社会招聘“0”基础的人来培养,倒不如从软件测试人员里升华!一般来说,这两种职位的上报对象是项目经理或相同级别管理者。
      转型后的配置管理与质量保证工程师,一定要转变一个意识,那就是常规测试工程师的工作范围很大一部分(不是全部)只限于测试流程,而配置管理和质量保证的工作范围是面向整个软件开发流程,二者的职业要求都非常重视软件工程知识体系的建立和软件开发总体流程的实施能力。由于配置管理工程师除了企业配置管理流程的搭建与实施外,一般会涉及配置管理工具的管理与维护,而质量保证工程师更多的工作是软件开发流程的控制与维护,故而配置管理对技术的要求稍高于质量保证。随着我国软件行业水平的不断发展,众多软件公司纷纷通过CMM/CMMI,企业对于软件开发团队的角色配比制度也将逐渐健全,当前社会对配置管理与质量保证工程师的职位需求日益增加,种种现象表明,对于软件测试工程师出身的从业者,转型至SCM/SQA不失为突破个人职业生涯瓶颈的又一通道!
      业务测试工程师,笔者定义为面向行业类软件业务逻辑与工作流测试的人员。当前软件开发类型,很大一部分是行业类软件的应用,如ERP、SCM、CRM、OA、电信、金融、财务、嵌入式、通信、手机、游戏……这就要求从事行业类软件测试的人员具备行业背景、业务知识,熟练该行业工作流程。从社会上出现的很多对此类经验要求的测试工程师招聘信息中,我们更加肯定这种趋势;所谓存在即是道理,既然社会上有了需求,那么就可以作为个人发展的方向。而另外一个特点是,业务测试工程师的工作内容主要是黑盒测试,属于功能范畴,因此对技术要求不大,设置一些大型行业类软件公司的业务测试工程师薪资丰厚,但是完全可以不懂技术,因为它的工作性质决定了不需要懂很多的技术!他们甚至连软件的界面测试都不做——交给常规测试工程师实施,而完全关注软件的业务性和易用性,由于其深厚的行业背景,可以为软件的在正式发布前提出很多建设性的意见,而这些建议正是软件开发商提高产品易用性、增加用户满意度、开拓市场、创造利润的关键因素之一!
      当管理路线的中级域方向继续上升至高级域,就分别到达配置管理经理、质量保证经理、产品经理、业务专家,这类人才地位高、待遇厚,一般资深的软件工程领域专家都聚集于此。
      如果说配置管理工程师、质量保证工程师更加侧重于配置管理流程、质量保证流程的实施与日常管理维护,那么配置管理经理、质量保证经理就是更侧重于配置管理流程、质量保证流程的建立与改进。一般在中小软件企业,可能没有这两个角色,而全部的配置管理或质量保证工作都由工程师担当;但是大中型软件企业对资深配置管理经理、资深质保经理求贤若渴。软件系统越庞大,软件开发团队规模就越庞大,软件开发流程中出现问题的几率就越高,高效管理软件开发流程,不断改进软件质量,是每个软件公司在技术上没有顾虑后的下一个急需攻破的难关!
       业务专家,属于行业内咨询、顾问的角色,已经几乎脱离了测试工作本身,而更多为企业的产品需求分析、设计、开发、测试等各个环节提供指导工作,其目的也是提高软件的易用性和稳定性,减少后期不必要的需求变更。该职位也同样在目前热点行业的大中型软件企业有所设立。
      产品经理,这个职位在很多企业有所设立,笔者认为它是质保经理的派生,只是它更侧重于软件在产品化之前的质量监控工作,包括软件开发流程、软件测试等技术与管理的各个方面。由于该职位在业内没有明显定义,而根据不同企业的职位定位不同,这里无法统一陈述。
      管理路线的最高发展域是咨询域,与技术路线的专家域类似,在配置管理、质量保证、软件产品化、行业领域达到高深造诣的人才,他们有丰富的从业经验、深厚的管理底蕴,具有对软件工程高瞻远瞩的慧眼和胆识,往往供职在专业的咨询与培训公司,提供IT业管理类咨询与培训的服务,推动着软件行业的前进。国内外很多为软件企业进行CMM咨询和实施的公司里,就是这些人才的大本营之一!
      笔者认为,在“双V模型”的管理路线里,中低级发展域的人才对技术与管理的区分较为明显,而到了高级与更高级发展域,更多的是复合型人才,软件业以技术为主导,没有一定技术积累,还是很难达到高级境界;要在管理路线练出“上乘武功”,还是希望大家在主攻管理与流程类课题的同时,多丰富下自身的技术层面,嘿嘿!
      另外,笔者提倡管理与技术两条路线的平齐,而并非目前社会上认为的技术要比管理低一等,技术是靠吃青春饭,在这些人才到达最高发展域的“咨询”与“专家”层面,二者应该完全具有相同的地位和待遇,只是“称谓”不同罢了!
      “双V模型”是sincky结合当前国内外软件测试行业现状提出的职业发展流程图,仅供测试从业者参考,并非一个“死”的框架,大家不要拘泥于流程图本身;其实目前国内很多上升到高级域或最高域的资深人才,很多都是跳跃式、甚至跨越式的职业发展,因为命运掌握在自己手里,任何人都剥夺不了设计自身人生蓝图的权利;而另外一个角度是,任何人都不该不珍惜为自己规划职业生涯的机会!
       软件测试,一个日出东方的国际型行业,虽然偶尔会弥漫晨雾,甚或有暴雨来袭,但是我们都该坚持!有人说:“什么叫失败?”答曰:“放弃就是失败!”每一次当我们身处逆境时,决不能用软弱的眼泪作为走向明天的见证,更不能用脆弱的感情去拴住生命的航线;是雄鹰就该搏击长空,是蛟龙就该挽起狂澜;沧海横流,方显英雄本色,疆场搏斗,可露壮士肝胆!人生没有豁免权,每位从业者只有怀着不息的斗志,乘千里长风,破万里巨浪,才能支配命运走向辉煌的明天!
       后记:sincky,网名叶赫华;在我从事软件测试培训业的1年多里,接触了国内很多除了我们学员以外的软件测试界朋友,其中新手居多。在我们的网站、论坛、我个人的blog、我的qq群乃至其他朋友以我名义建的qq群里,最让大家感冒的话题就是测试人员的职业发展!大家都在做测试工作,可以不知道明天做什么,明年做什么,或者若干年后做什么!“行有行规”,除非不在软件测试这个行业,否则就要遵守这个行业的规律!我觉得,我们的学员有职业发展培训课程,可是面对外界这些热心朋友的提问,长久以来,我一直想集中的写点东西,起码让刚入行的新手对这一行业的职业发展方向有一个直观的感性认知,我也心满意足!但是这个行业还太嫩,并没有章据可循,我搜索了几天的国外网站,可是没有成文的观点可以参考!后来决定自己来写,参照的对象就是国内现状下的测试从业者,于是在和国内各个领域的测试高手朋友们的交流后,我在2006春节前夕用了一白天的时间画出了“双V模型”图,而这篇文章的撰稿,用了我一天一夜的满满时间。在经历几次修改后,这篇文章在今天终于正式发布了,没有别的,只是希望给国内测试界朋友一个参考,欢迎大家批评、讨论,发表自己的观点!(我的msn地址:sinckyzhang@gmail.com)“双V模型”里很多职位名词在国内叫法不一,比如有人把初级测试工程师叫做测试员,我不赞同这种叫法,毕竟不是主流;而我的目的,只是通过这些职位的工作内容来告诉大家在职业定位上需要达到的高度,名字嘛,只是个代号而已!
  • 【转】测试职业发展道路[1]

    2009-06-06 13:48:31

    天地玄黄,宇宙洪荒;所谓光阴似箭,因为一转眼滚滚的历史车轮就将人类文明推进了二十一世纪的信息时代!葛大爷有对白曰:“二十一世纪最宝贵的是什么?”对曰:“人才!”何为人才?sincky曰:“适应时代潮流,把握社会需求,并为我中华老大帝国创造社会价值的人!”哎哟,不诹了,其实今天笔者在这里要和大家探讨的,是软件测试的职业发展问题,重点要阐述的是软件测试从业者的职业发展方向,欢迎大家按enter键换行,继续浏览!
      一个人从大学毕业,即开始发生从学生时代向职业人士的过渡,这种过渡走的好,可以实现毕生宿愿,体现个人价值,不管你是否喜欢,功名、利禄尽收眼底;如果走的不好,则会误入歧途,纵有凌云壮志、万丈豪情,难免一生郁郁不得志,终归化作片片飞尘,无语对穹苍!那么如何才能顺利的完成这种过渡、踏上我们豪迈的职业旅程呢?答曰:认清自己,选择适途!战国的魏人荆轲具有“十步杀一人,千里不留行”的本领,曾向魏王献策曰:“国君,我是职业杀手,我杀人的技术很强!”魏王问:“那么你想杀谁呢?”对曰:“杀他个国君如何?”魏王大惊,慌然离去!后来荆轲离开魏国,与燕太子丹密谋,留下了“图穷匕首见”、“荆轲刺秦王”的千古佳话。荆轲,良禽也,择木而栖和太子丹合作,是他的高明之处;不过笔者认为他是一个典型的“低管理、高技能”的人才,当他紧握嬴政的脖领、持剑相逼时,他太得意忘性了,可见他没有领导的“统御力”和“决断力”,所以落了个刺杀失败、拔剑自刎的下场,虽然他的侠义与胆识流畅千古,但是终究是个“杀手”而已;当今社会下,如果“低管理、高技能”的人干工作干到丢了性命,那也真是一个笑谈了!
      目前我们国家高等学历大幅度扩招,造成社会的低端人才严重过剩,大学生毕业找不到工作、或者找不到合适的工作例子鳞次栉比;但是社会各行各业对高端人才的需求又求贤若渴;那么如何解决这种矛盾呢?从大环境来说,国家应该改革教育体制、提高教学质量、重视高端人才的培养,但是,一个问题一旦上升到国家的层次,就要等它个十年八年!我们没有办法改变世界,但是我们有能力改变自己;所以我们从个人的角度来讲,讲讲我们这些软件测试的从业者们,如何“认清自己、选择适途!”
      纵观当今社会各行各业,对于个人的职业发展方向,从宏观上都可以划分为四个群体,即:
      “低管理、低技能”
      “高管理、低技能”  
      “低管理、高技能”
      “高管理、高技能”
      而在IT 行业这种划分方法更为合理,sincky为其命名为“一起点-三方向示意图”:
      告别了象牙塔,带着对校园生活里那段风花雪月的追忆,年轻的毕业生们走上了社会;这时候的年轻人,大多数是属于“低管理、低技能”的群体,我们没有工作经验,不知道企业的工作流程,不清楚各个职业的工作技能,更不具备任何行业的管理能力;然而值得庆幸的是,人类问明发展到现在所出现的众多行业,都已经有了众多可以参考的群体,这些群体就理所当然的成了我们可以借鉴的发展方向!虽然我们的起点都是一个,但是可以选择的发展方向却是丰富多样!
      高管理-低技能,即是我们通常所说的管理路线!在IT业,这个方向的成功者不乏项目经理、项目总监直至企业的最高管理层;但是走这个方向也要有技术方面的积累,因为管理者的影响力中,除了职位赋予的权力以外,还包括个人人格方面的能力和专业领域的专业能力,而后者就是技术水平!而计算机行业本身,也决定了技术底蕴对职业发展的重要影响,所以年轻的IT朋友们,如果想为自己的职业人生设计成这个路线,除了适当的技术积累外,更要有意识的锻炼自己的管理素质,下图可做参考:
      低管理-高技能,即通常所说的技术路线!IT业以技术为主导,对于喜欢钻研技术、探讨技术的人,可以选择该条路线,走的深入、走的彻底!只因中国对于技术与管理的认识不同,造成很多人认为做技术不赚钱、不被重视,自身误以为不过是个工程师而已,所做事情只是辅助企业的运作。实际上,在欧美发达国家,资深技术人员的薪资非常高,从业时间的周期也相当长,在Microsoft、IBM等巨头企业,不乏年龄在50岁以上的资深程序员或系统架构师,而其薪资也和高级管理者一样高!而另外一个不争的事实是,企业对于管理的职位是有限的,并且一些优秀的技术人员不愿做管理,或者不适合做管理,因此社会上出现的资深技术专家(或者类似职位),为喜好技术的从业人员提供向上的通道。
      高管理-高技能,即咨询方向是较为均衡、全面的路线,也是众多企业希望员工努力的方向。然而有调查结果显示,由于现实种种因素的制约,大约90%的个人是分别沿着管理方向或者专家方向发展的,真正实现在咨询方向达到一定的高度的人少之又少,而且在这为数不多的咨询方向达到又一定高度的人才,往往又会由于企业资源的限制无法将个人价值完全发挥而最终离开所在企业,成为专业培训师、咨询师;一些国际知名的咨询公司如麦肯锡、安达信乃至毕博或其他,可谓大家在个人职业生涯到达一定阶段,作为自己继续突破职业瓶颈的发展路线。
      那么,对于软件测试的从业者,我们的出路在哪里?我们的职业发展该如何设计?我们的发展方向又有哪些呢?这里笔者和大多数测试同行意识相同,笔者也曾在多篇文章里标明,中国的软件测试行业尚属起步阶段,其发展的步履上布满了荆棘与泥泞;然而其发展速度可谓惊人的,从笔者刚毕业时候对软件测试的“0”概念、从业同行者寥寥无几,到最近2年的各大媒体纷纷报道的中国软件测试人才缺口20万、软件测试工程师将成为未来10年最紧缺的人才之一、包括笔者所接触的众多国内外优秀企业对高端测试人才年薪10万、15万、20万的招聘需求……可见,选择软件测试这个朝阳行业的朋友,做了一个比较正确的选择!然而,如何任何事物总有它的两面性和矛盾性:2006年初在北京、上海、深圳举办的几次春季大型招聘会上,多家企业纷纷打出各类高薪招聘软件测试人员的海报,出人意料的是,收到的简历尚不足招聘岗位数的50%,而合格的竟不足30%……引起我们思考的是,我们的软件测试从业人员还有很大一部分不满足当今社会的需求;而另一层含义是,我们还有很大的提升空间!因此解决该矛盾的突破点是:每个人在这个行业里找到自己的发展方向,规划自己的职业蓝图,从而有针对性的锻炼自己的职业技能,增加个人的职业砝码!
      软件测试职业发展方向,大体上与上述的通用职业发展路线图相吻合,也可以分为管理路线、技术路线、管理+技术路线;只是针对该行业本身,有其特殊性和细致性。其图示如同两个重叠的”V”字样,我们为其命名为“双V模型”;该模型适用于大多数行业性软件测试从业人员,一些特殊领域如游戏测试、嵌入式测试、硬件测试,也可作为参考。本文是三部曲之一,只介绍职业发展方向定义,在下一曲会介绍各个职业方向应该具备的知识与技能体系!
      双V的底点是测试工程师,属于软件测试职业生涯的初级域,其适用范围是入行软件测试3年内的常规测试从业者,其主要工作内容是按照测试主管(即直接上司)分配的任务计划,编写测试用例、执行测试用例、提交软件缺陷,包括提交阶段性测试报告、参与阶段性评审等。初入测试行业,进入企业从事测试工作的人员,都要从该层次做起,虽然有时感觉乏味无趣,甚至迷茫困惑,但是我们可以根据个人的兴趣与特长,向上选择适合自己的路线,因为谁都不会甘心一辈子只做一个普通的测试工程师,那么大家看到这里,就可以摩拳擦掌,看看向上发展的通道中,哪一个适合自己,然后立刻从现在开始,确定自己未来5年、10年甚至一生的发展目标迈进,用笔者经常跟学员说的一句话来形容:把握现在,即刻做起,相信自己是最强的!
      首先是常规路线,即双V模型的重叠线,这条发展路线要求管理与技术并重,因为软件测试的行业特点决定了这个因素:测试工程师向上晋升到测试主管、测试经理、测试总监,直至咨询域的更高方向!
      测试主管是企业项目级主管,对于中小型软件公司也可以是企业级主管,属于中级发展域,适用范围是2到5年职业经验的测试从业者。其工作内容是根据项目经理或测试经理的计划安排,调配测试工程师执行模块级或项目级测试工作,并控制与监督软件缺陷的追踪,保证每个测试环节与阶段的顺利进行。严格来说,这个级别更多属于测试的设计者,因为企业的测试流程搭建是由更高级别的测试经理或相关管理者来做的,测试主管负责该流程的具体实施;而更多的工作,是思考如何对软件进行更加深入、全面的测试。因此笔者认为测试主管比较有创造性的工作内容就是测试设计,而恰恰很多公司忽略了或没有精力来执行此工作内容!应该说,在一个企业里做了3年左右测试工作的人员,很容易晋升到该职位,而之所以晋升,是与个人测试技术的过硬、测试方法的丰富,加上对测试流程的监控力与执行力的职业素质息息相关!
      测试经理是更高级别的测试管理者,属于高级测试方向域。对于大中型软件公司,该职位尤为重要,并且对其职业要求也比较高,一般适合4到8年的测试从业者,在管理与技术能力双双比较成熟的情况下,可以结合具体环境晋升到该级别。测试经理负责企业级或大型项目级总体测试工作的策划与实施。随着软件行业的发展,企业对软件工程里各个角色的定位逐渐明显,测试经理完全与开发经理(一些公司也成为项目经理)平齐,除了需要统筹整个企业级或项目级测试流程外,还要对于不同软件架构、不同开发技术下的测试方法进行研究与探索,为企业的测试团队成员提供指导与解决思路,同时还要合理调配不同专项测试的人力资源(如业务测试工程师、自动化测试工程师、白盒测试工程师、性能测试工程师),对软件进行全面的测试;另外,一些企业里,测试经理还需要与客户交流与沟通,负责部分的销售性或技术支持性工作。嘿嘿,看看那些高薪招聘测试经理的企业对该职位的要求里外语口语的描述,就可见一斑!
      测试总监,属于常规发展路线的最高域,如果再往上发展,那只能是咨询域了;不过笔者并没有将其在图中标记出来,因为该职位对于国内目前的大多数软件公司根本没有设立,也就没必要再在图中体现了。该职位一般在大型或跨国型软件企业,或者专向于测试服务型企业有所设立,由于其企业自身的职位定位不同,以及软件测试整体行情所处的阶段,这里不好归纳陈述;但是一般设立测试总监的企业,该职位都相当于CTO或副总的级别,是企业级或集团级测试工作的最高领导者,驾驭着企业全部的测试与测试相关资源,管理着企业的全部测试及质量类工作。而其职业要求,也是技术与管理双结合;基于目前软件测试行情看,这种高管理-高技能的发展目标,不会适合大多数人的选择,社会也不会提供如此众多的测试总监职位让我们去应征!
      应该说,大多数测试从业者都不是技术与管理双优的人,而如今一些到达测试经理或测试总监级别的优秀测试人才,已经领先一步开辟了这条发展路线的先河,希望这些朋友和大家多多分享经验,让更多的朋友弥补自己管理或技术上的不足,在这条路线上有所建树,共同提高,在实现个人人生价值的同时,也自然而然的推动了软件测试行业的发展;行业发展了,测试人员不再被忽视了,待遇自然也提高了,也就不会有很多朋友迷茫的跟我说“我的日常工作只是点击按钮和按键盘”了,因为我们相信行业的不断成熟,会逐渐将软件测试职业细化,我们的从业者就可以真正的在如下的管理路线和技术路线找到自己的位置,并潜心走向深入的!
      软件测试,是技术主导的职业;不管选择哪条发展路线,都是需要一定的技术沉淀,只是相对来说,管理路线对技术方面要求不高而已。那么我们就先挑重头的技术路线展开讨论。一般来说,一个普通的测试工程师刚入行,3个月左右熟悉企业的工作流程和模式,那么今后的工作内容趋于平稳。然而社会是残酷的!如果单单停留在测试工程师的阶段,若干年后,相信你再也竞争不过那个时候的应届毕业生,当你的工作技能和职业素质趋于与那些朝气蓬勃的年轻人相当时,企业会毫不留情的选择他们,而release你,因为你的成本消耗要比他们高,这是大实话!然而现实又是公平的!因为软件开发技术的不断日新月异,软件功能需求的不断丰富多样,决定软件开发这一系统工程的错综复杂,因此为了保证软件的质量,就要提高测试的水平,这也就为软件测试职业的细化起到先决因素,也是目前社会上出现招聘专项测试工程师的必然趋势!因此,这个趋势给了我们这些常规测试工程师一个空前的好机会!所谓“以毒攻毒”,软件开发靠的是技术,为了测试软件,也必须用技术;那么我们就来看一下从技术路线,软件测试职业发展有哪些方向。
      技术路线,笔者结合国内外软件测试行业现状,划分为三个半方向,分别是自动化测试工程师、白盒测试工程师、性能测试工程师和认证测试工程师,在“双V模型”中右侧体现;前三者适用于通用软件测试领域,认证测试工程师乃嵌入式测试领域职位,至少目前仅出现在嵌入式领域,因此以虚线标记,即“三个半”的“半”。前三条路线对技术的要求程度逐渐增加,三条曲线的斜率也依次递增(认证工程师不参与比较)。
      自动化测试工程师,笔者为其定义在功能测试范畴,指通常所说的依靠自动化测试工具进行软件黑盒测试的工程师。笔者接触的很多测试界朋友,尤其年轻的刚入行者,对测试工具充满了无限的兴趣,他们喜欢那种编写脚本、调试成功后的快感,喜欢看到自定义的日志里记录了本来手工测试烦琐的无聊头顶的工作、而采用自动化方式实现后如此清晰丰富的内容后的兴奋!可以理解,因为笔者也是从那段时光走过来的,现在也负责于我们学员的自动化测试教学工作。从大环境讲,自动化测试是软件测试执行阶段的必然趋势,社会对于软件测试的认可度以及对自动化测试人才的需求必将日益增加,从目前国内做自动化测试的从业者薪资情况看,也普遍高于常规测试工程师,最浅显的道理是“自动化测试比手工测试有了技术含量,^--^”虽然自动化测试在整个行业的普及不是一朝一夕,但是从个人角度讲,自动化测试可以作为个人的发展方向之一,因为如果你率先掌握了这种技术,等到社会需要时,你已成为这个职位的成熟操作者!而国内的51testing把握了时代前沿,与自动化测试工具巨头厂商Mercury(美科利)合作,在中国唯一推出Mercury自动化测试全套技能认证(CPE/SP/CPC),相比其它初等认证,它的实效性和价值性更具意义,也为测试从业者提供了一个进入自动化测试领域的快捷方式!
      白盒测试工程师,笔者定位于在软件测试周期的单元测试阶段对软件进行的代码级测试的人,包括代码走读、代码功能与逻辑测试、代码内存泄漏检查、代码运行效率检查、代码测试覆盖率分析等。如果说,自动化测试只是依靠脚本语言完成测试脚本编写与调试的过程(因为自动化测试工程师的工作重点不在编写脚本),对于自动化测试工程师的技术要求要相对偏低的话,那么白盒测试工程师就要对大型程序开发语言的完全掌握,因此其技术要求相对偏高!而另一方面,白盒测试在目前国内软件行情下,一些公司根本不做,其成本高、代价大的特点决定了这个现状,而一些对软件质量要求非常高(如军事类、电信类、财务金融类等)的企业,也会调动开发工程师来实施此事。但是,还是那句话,测试行业在发展,测试人员能力在提升,软件的开发技术在复杂化,要对软件进行尽可能全面的测试,白盒测试不可忽视!当下专门高薪招聘白盒测试工程师的企业也比比皆是,从中我们可以感知,白盒测试工程师会是很多有开发背景、意欲进入测试行业的良好突破口,白盒测试人员的需求也会逐渐增加。
      性能测试工程师,即在系统测试阶段、功能测试后对软件系统性能指标进行采集分析和运行效率检测的人。笔者认为,在一个尽量压缩的测试流程里,功能测试可以手工进行,白盒测试可以不做,但是性能测试必须要做,除非该软件非网络类软件即单机版软件!这里笔者再提一个观点供大家参考:软件测试,从宏观上可以划分为三个大方面:功能测试、性能测试、安全性测试,功能测试说明软件做对了,功能测试+性能测试说明软件做好了,三者结合起来说明软件做的非常好!安全测试暂且抛之不提,这是下一个发展域的内容,但是为了把软件做好,为了真正保证软件的质量,性能测试绝不容忽视;只因目前很多企业由于时间、成本、人力条件的限制,暂且不做性能测试。性能测试工程师相对来说,是三个技术路线里技术要求最高的,因为软件的性能瓶颈归根结底落实到代码的运行效率这个问题上,因此性能测试要做好,性能测试工程师起码要懂开发;而为了发现性能问题,要懂软件开发架构;为了定位性能问题,要懂操作系统、网络协议、应用服务器乃至数据库的原理与使用;为了最终解决性能问题,要根据定位的问题有针对性的对代码、操作系统、网络架构、服务器、数据库进行优化!当然性能测试是一个系统工程师,绝对不是一两个人的事情,对于常规性能测试工程师,具备定位性能问题的能力即可。正因为性能测试工程师技术要求的高超,该职位的待遇也是目前测试技术路线最高薪的一个,实为综合技术能力较强的测试人员的明智选择!
      上述四职业路线由于其技术程度的突出,一般在企业里由测试经理直接所属,与测试主管级别具有相同的待遇,并处于相同发展域。
  • 测试报告模板

    2009-04-17 10:27:01

  • SQL的一些总结

    2009-03-04 21:18:07

    SQL分类:
    DDL—数据定义语言(CREATE,ALTER,DROP,DECLARE)
    DML—数据操纵语言(SELECT,DELETE,UPDATE,INSERT)
    DCL—数据控制语言(GRANT,REVOKE,COMMIT,ROLLBACK)
    1、说明:创建数据库
    CREATE DATABASE database-name
    2、说明:删除数据库
    drop database dbname
    3、说明:创建新表
    create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)
    根据已有的表创建新表:
    A:create table tab_new like tab_old (使用旧表创建新表)
    B:create table tab_new as select col1,col2… from tab_old definition only
    4、说明:删除新表
    drop table tabname
    5、说明:增加一个列
    Alter table tabname add column col type
    注:列增加后将不能删除。DB2中列加上后数据类型也不能改变,唯一能改变的是增加varchar类型的长度。
    6、说明:添加主键: Alter table tabname add primary key(col)
       说明:删除主键: Alter table tabname drop primary key(col)
    7、说明:创建索引:create [unique] index idxname on tabname(col….)
    删除索引:drop index idxname
    注:索引是不可更改的,想更改必须删除重新建。
    8、说明:创建视图:create view viewname as select statement
    删除视图:drop view viewname
    9、说明:几个简单的基本的sql语句
    选择:select * from table1 where 范围
    插入:insert into table1(field1,field2) values(value1,value2)
    删除:delete from table1 where 范围
    更新:update table1 set field1=value1 where 范围
    查找:select * from table1 where field1 like ’%value1%’ ---like的语法很精妙,查资料!
    排序:select * from table1 order by field1,field2 [desc]
    总数:select count * as totalcount from table1
    求和:select sum(field1) as sumvalue from table1
    平均:select avg(field1) as avgvalue from table1
    最大:select max(field1) as maxvalue from table1
    最小:select min(field1) as minvalue from table1
    10、说明:几个高级查询运算词
    UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2。
    EXCEPT 运算符
    EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。
    INTERSECT 运算符
    INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。
    11、说明:使用外连接
    A、left outer join:
    左外连接(左连接):结果集几包括连接表的匹配行,也包括左连接表的所有行
    QL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
    右外连接(右连接):结果集既包括连接表的匹配连接行,也包括右连接表的所有行。
    full outer join:
    全外连接:不仅包括符号连接表的匹配行,还包括两个连接表中的所有记录。

    其次,大家来看一些不错的sql语句

    1、说明:复制表(只复制结构,源表名:a 新表名:b)
    法一:select * into b from a where 1<>1
    法二:select top 0 * into b from a
    2、说明:拷贝表(拷贝数据,源表名:a 目标表名:b)
    insert into b(a, b, c) select d,e,f from b;
    3、说明:跨数据库之间表的拷贝(具体数据使用绝对路径)
    insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ where 条件
    例子:..from b in '"&Server.MapPath(".")&"\data.mdb" &"' where..
    4、说明:子查询(表名1:a 表名2:b)
    select a,b,c from a where a IN (select d from b )
    或者: select a,b,c from a where a IN (1,2,3)
    5、说明:显示文章、提交人和最后回复时间
    select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title)
    6、说明:外连接查询(表名1:a 表名2:b)
    select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
    7、说明:在线视图查询(表名1:a )
    select * from (SELECT a,b,c FROM a) T where t.a > 1;
    8、说明:between的用法,between限制查询数据范围时包括了边界值,not between不包括
    select * from table1 where time between time1 and time2
    select a,b,c, from table1 where a not between 数值1 and 数值2
    9、说明:in 的使用方法
    elect * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’)
    10、说明:两张关联表,删除主表中已经在副表中没有的信息
    delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 )
    11、说明:四表联查问题:
    select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....
    12、说明:日程安排提前五分钟提醒
    SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())>5
    13、说明:一条sql 语句搞定数据库分页
    select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段
    14、说明:前10条记录
    select top 10 * form. table1 where 范围
    15、说明:选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.)
    select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)
    16、说明:包括所有在 TableA 中但不在 TableB和TableC 中的行并消除所有重复行而派生出一个结果表
    (select a from tableA ) except (select a from tableB) except (select a from tableC)
    17、说明:随机取出10条数据
    select top 10 * from tablename order by newid()
    18、说明:随机选择记录
    select newid()
    19、说明:删除重复记录
    Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...)
    20、说明:列出数据库里所有的表名
    select name from syscolumns where id=object_id('TableName')
    21、说明:列出表里的所有的
    select name from syscolumns where id=object_id('TableName')

     

    二.资料形态 datatypes

    smallint

    16位元的整数。

    interger

    32位元的整数。

    decimal(p,s)

    p精确值和s大小的十进制整数,精确值p是指全部有几个书(digits)大小的值,s是指小数后有几位数。如果没有特别指定,则系统会设为p=5;s=0.

    float

    32位元的实数。

    double

    64位元的实数。

    char(n)

    n长度的字符串,n不能超过254.

    varchar(n)

    长度不固定且其最大长度为n的字串,n不能超过4000.

    graphic(n)

    和char(n)一样,不过其单位是两个字元double-bytes,n不能超过127,这个形态是为了支援两个字元长度的字体,例如中文。

    vargraphic(n)

    可变长度且其最大长度为n的双字元字串,n不能超过2000.

    date

    包括了年份、月份、日期。

    time

    包含了小时、分钟、秒。

    timestamp

    包含了年 月 日 时 分 秒 千分之一秒。

  • QTP基础及描述性编程

    2009-03-04 17:34:03

    我们简单介绍一下有关功能测试的基本方法,这实际上对于所有自动化功能测试产品来说都是一样的。一般情况下,用QTP来进行功能测试的基本方法主要包括三个主要阶段: 

    1、创建测试或组建

    首先可以通过在应用程序或网站上录制会话,或者建立对象库并使用关键字驱动功能向关键字视图中手动添加步骤来创建测试或组件。在QTP里面我们可以通过两种方式添加步骤来创建测试或组件:

    在应用程序或网站上录制会话。
    建立对象库并使用这些对象在关键字视图或专家视图中手动添加步骤
    然后在在测试或组件中插入检查点,检查页面、对象或文本字符串中的特定值或特征,通过它可以标识网站或应用程序是否正常运行。或是通过用参数替换固定值扩展测试或组件的范围。提供数据表中的数据,定义环境变量和值,定义测试、组件或操作参数和值,或者使用QTP生成随机数字或当前用户和测试数据等。

    最后如果需要的话使用QTP中众多的功能测试功能来增强测试或组件或添加编写语句来实现更复杂的测试目标。

    2、运行测试和组建

    控制运行会话,帮助标识和消除测试或组件中的缺陷。使用“单步执行”、“单步跳过”和“单步退出”命令逐步运行测试或组件,或设置断点使测试或组件在预定点暂停。每当测试或组件在断点处停止时,可以在“调试查看器”中查看其变量的值。

    3、分析结果

    在运行测试或组件之后,通过两种方式可以查看其结果:在“结果”窗口中查看结果;自动报告在运行会话过程中检测到的缺陷,可能的话并上报到其他缺陷管理产品中。

    .试图与对象模型

    在介绍QTP中的Descriptive Programming前,我们有必要先介绍一下ExpertView及在ExpertView里进行编码的一些基本知识。

    在QTP里面提供了两种视图,第一种我们把它称为KeywordView(关键字视图,在早期的版本中称为TreeView),第二种把它成为ExpertView(专家视图),这两种视图分别是针对两种类型的人进行使用的。

    1、KeywordView(关键字视图)

    通过关键字视图,QTP提供了一种模块化的表格格式创建和查看测试或组件的步骤。每个步骤在关键字视图中都是一行,这样用户可以轻松的修改任何一部分组成。

    在录制会话过程中,用户在应用程序上执行的每个步骤在关键字视图中记录为一行。例如,在51testing的页面上执行的下列三个步骤:

    在“用户名”编辑框中输入zhoda02。
    在“密码”编辑框中输入加密字符串41c630a213508cd49eb35089db1b893144b9。
    单击“登录”按钮。
    那么,关键字视图将包含下列行:



      

    很显然,关键字视图非常直观有效,使用的人可以很清晰的看到被录制对象的录制层次及运行步骤,比较适合那些对于业务操作流程熟悉的人员使用。但是,如果需要一些增强型的操作,那就需要切换到专家视图里进行了。

    2.ExpertView(专家视图)

    QTP在关键字视图中的每个节点在专家视图中对应一行脚本。上面例子对应的脚本如下:(删除后一句是因为前后重复,一句可以说明问题

    Browser("51Testing软件测试网:软件测试的专业网站").Page("51Testing软件测试网:软件测试的专业网站").WebEdit("username").Set "zhoda02"

    Browser("51Testing软件测试网:软件测试的专业网站").Page("51Testing软件测试网:软件测试的专业网站").WebEdit("password").SetSecure "41c630a213508cd49eb35089db1b893144b9"

    Browser("51Testing软件测试网:软件测试的专业网站").Page("51Testing软件测试网:软件测试的专业网站").WebButton("登录").Click

    对于QTP来说,其核心编码语言是Visual Basic Script,因此,如果用户熟悉VBScript,可以运用自如的添加和更新语句,并通过编程方式增强测试和脚本,而这一切必须在专家视图中完成。

    更为重要的是,有些操作是必须在专家视图中才可以完成的,例如:要处理动态对象、客户化报告、获取对象运行时的属性值(Run-time Value)等等,这些都必须通过专家视图中的VBScript编码完成。

    然而,QTP里所有的操作都是基于对象进行的,所以我们必须对对象模型有一个基本了解,才可以在专家视图内进行Descriptive Programming。

    3、测试对象模型

    测试对象模型是一大组对象类型或类,QTP用这些对象类型或类来表示应用程序中的对象。每个测试对象类都有一个可以唯一标识属于该类的对象的属性列表,以及一组QTP可以对其进行录制的方法。它包括测试对象(Test Object)和运行时对象(RunTime Object)。

    测试对象是QTP在测试或组件中创建的用于表示应用程序中的实际对象的对象。QTP存储有关该对象的信息,这些信息有助于它在运行会话期间标识和检查该对象。

    运行时对象是网站或应用程序中的实际对象,在运行会话期间执行针对该对象的方法。

    如果录制时执行应用程序的相应操作,则一般情况下QTP将完成以下操作:

    标识QTP测试对象类(表示执行了操作的对象),并创建相应的测试对象
    读取应用程序中对象属性的当前值,然后将属性和属性值列表与测试对象一起存储。
    选择该对象的唯一名称,一般使用该对象某个重要属性的值。
    使用适当的QPT 测试对象方法录制对对象执行的操作。
    例如,假定使用以下HTML 源代码单击“查找”按钮:

    <INPUT TYPE="submit" NAME="Find" VALUE="Find">

    QTPl将单击的对象标识为WebButton 测试对象。它将创建一个名为Find 的WebButton 对象,然后为该Find WebButton 对象录制下列属性和属性值,同时还会录制对WebButtion的Click方法。



      

    在关键字视图及专家视图中显示内容分别为:



      

    Browser("Mercury Interactive").Page("Mercury Interactive").WebButton("Find").Click

    运行测试或组件时,QTP通过其测试对象类及其描述(一组用于唯一标识该对象的测试对象属性和属性值)来标识应用程序中的每个对象。测试对象及其属性和属性值的列表存储在对象库中。例如在上例中,QTP将在运行会话期间在对象库中搜索WebButton 对象,通过名称Find 来查找其描述。QTP根据找到的描述,在应用程序中查找WebButton 对象,该对象带有HTML 标记INPUT、类型为submit、值为Find。找到对象后,它将对其执行Click 方法。

    在这样一组对象模型的基础上,QTP为各类应用对象都提供了一组方法和属性,例如Web Objects,Windows Objects,SAPGUI Objects,ActiveX,Java等。下面是一些Web Objects的方法和示例:

    对象
    方法

    Browser
    Check

    Frame
    Click

    Image
    Exist

    Link
    GetCellData

    Page
    GetProperty

    WebArea
    GetROProperty

    WebButtion
    Mouseover

    WebCheckBox
    RowCount

    WebEdit
    Select

    WebList
    Set

    WebRadioGroup
    SetProperty

    WebTable
    Submit

    例1:获取单元格中的值

    thisText = Browser(…).Page(…).Frame.(…).WebTable("sample").GetCellData(2,1)

    例2:获取图片的名称

    ObjectName = Browser(…).Page(…).Image("Find").GetProperty("Name")

    例3:检查某个对象是否存在,如果存在弹出对话框说明对象存在。

    If Browser("Browser").Page("Page").Applet("login.html").JavaEdit("username").Exist Then

    MsgBox("The object exists.")

    End if

    .描述性编程(descriptive programming)

    1、descriptive programming概述

    通常情况下,当在录制一个操作时,QTP会将被操作对象加入到对象库里(Object Repository)。一旦对象存在于对象库里,我们就可以在专家视图里通过添加相关的对象方法来对该对象进行操作。我们可以通过引用层次型对象库里的对象描述(Object Description)来添加相应的方法。

    因为QTP对象库中的每个对象都具有唯一名称,所以在引用时对象名是必须需要指定的。然后在测试运行期间,QTP在对象库中根据这个对象的名称和父对象来查找对象,并使用为这个测试对象存储的测试对象描述,在网站或应用程序中标识该对象。

    例如我们用QTP录制Yahoo Mail登录情况时我们需要输入用户名,于是在录制时我们就会录下一个WebEdit对象,它的缺省逻辑名为“login”,该编辑字段位于名为“Yahoo! Mail - The best” 的页面上,并且该页面在浏览器中使用名称Yahoo!进行录制。如图所示,即为录制时的对象库的内容:



      

    那么如果我们想要应用该对象,就可以在专家视图输入以下信息:

    Browser("Yahoo!").Page("Yahoo! Mail - The best").WebEdit("login").Set “xxx”

    或者我们也可以调用一些方法,获取改对象在运行时的对象名,如:

    Browser("Yahoo!").Page("Yahoo! Mail - The best").WebEdit("login").GetROProperty(“name”)

    然而,我们可以发觉到,上面的例子在处理对象时,对象已经存在于对象库里,因此我们可以应用这个对象的逻辑名。实际使用中,情况往往并非如此简单,我们经常会遇到很多在页面上动态产生的对象,换而言之,对象库里没有这些对象,我们也无从引用。因此我们必须采用其他的技术来完成这类操作,这也就是我们需要讲解的Descriptive Programming。

    为了满足上面提到的动态对象的处理问题,QTP允许用户通过将对象属性编码到测试脚本里来动态识别对象,这就是我们通常意义下称为的Descriptive Programming。通过这种方式,我们可以指示QTP不通过引用对象库和对象名来对实际对象进行操作。具体操作中,我们只需要为QTP提供对象的一组属性和值,这样QTP就可以来识别相应的对象并对其进行操作。这相当于,告诉QTP要识别对象的一些关键特征,根据这些特征QTP就可以一一匹配然后识别出来这个对象。

    而且,更为重要的是,通过这种Descriptive Programming的方式,还可以让QTP识别具有某些相同属性的对象。我们先来举个例子来看一下:我们假设当前的Windows系统中打开了若干的Yahoo主页面(多于一个),现在我们要关闭所有的正在浏览Yahoo主页面的浏览器。

    对于上面那个例子来说,我们先看一个简单一点的情况,假设只有且仅有一个Yahoo主页面:那么我们可以用下面的方法来

    Window("Text:=Yahoo! - Microsoft Internet Explorer").Close

    我们可以看到语句里我们要查找的对象是Window窗口标题为“Yahoo! - Microsoft Internet Explorer”,然后把它关闭,具体的语法说明我们稍后为解释。但是上面的语句仅仅适合前面提到的条件“只有且仅有一个Yahoo主页面”,如果有多个同样的窗口就会出错,原因是通过语句可以匹配到多个对象,而QTP不知道应该对哪个对象进行关闭动作。我们需要进一步的缩小匹配范围:

    Dim i

    i = 0

    while (Window("Text:="Yahoo!" - Microsoft Internet Explorer", "index:="&i).exist)

    Window("Text:=Yahoo! - Microsoft Internet Explorer", "index:="&i).close

    i = i +1

    wend

    这里我们可以看到,对于具有相同属性的对象,我们可以通过index参数来对其进行区别,第一个对象为index=0,第二个为index=1等等,依次类推。当然我们还可以通过CreationTime和Location参数来定位对象,这里就不详细叙述了。

    通过上面的例子,我们对Descriptive Programming有一个基本了解了,下面我们详细讲解一下Descriptive Programming:在具体实现中,我们有两种类型的Descriptive Programming方法。可以列出直接在测试语句中描述对象的属性和值的集合;或者向Description 对象中添加属性和值的集合,然后在语句中输入Description 对象的名称。下面我们分别举例介绍。

    2、直接在语句中输入编程描述

    通过多个指定描述对象的property:=value对,可以直接在语句中描述对象,这是最直接有效的方法。

    常规语法为:

    TestObject("PropertyName1:=PropertyValue1", "..." , "PropertyNameX:="PropertyValueX""}

    TestObject - 测试对象的类。

    PropertyName:=PropertyValue - 测试对象的属性及其值。各个property:="value" 对之间应用逗号和引号分开。

    例如:以下语句指定Mercury Tours 页面中名为author且索引值为3 的WebEdit 测试对象。当测试运行时,QTP 将查找具有匹配属性值的WebEdit 对象,并输入文本jojo。

    Browser("Mercury Tours").Page("Mercury Tours").WebEdit("Name:="Author"", "Index:="3"").Set "Mark Twain"

    我们也可以从从描述中的特定位置(从Page 对象描述开始)开始使用Descriptive Programming。

    Browser("Mercury Tours").Page("Title:="Mercury" Tours").WebEdit("Name:="Author"", "Index:="3"").Set "jojo"

    此外,如果我们希望在在一个测试或组件中多次使用相同的Descriptive Programming,则可以将创建的对象赋值给变量,这样使用会方便很多。

    例如:我们需要完成下面一系列操作

    Window("Text:=HyperSna").WinButton("Caption:=日期").Click

    Window("Text:=HyperSna").WinButton("Caption:=时间").Click

    Window("Text:=HyperSna").WinButton("Caption:=确定").Click

    那么,为了方便其见,我们可以将Window("Text:=HyperSna")赋值到一个变量,然后再使用,参见下面的代码:

    Set WinHyper = Window("Text:="HyperSna"")

    WinHyper.WinButton("Caption:=日期").Click

    WinHyper.WinButton("Caption:=时间").Click

    WinHyper.WinButton("Caption:=确定").Click

    如果使用了VBScript里面的With语句,还可以简化为以下代码:

    With Window("Text:="HyperSna"")

    .WinButton("Caption:=日期").Click

    .WinButton("Caption:=时间").Click

    .WinButton("Caption:=确定").Click

    End With

    下面我们来看一个更为详细的例子,在QTP产品缺省安装里面自带了一个网上订机票的示例称为Mercury Tour,我们看一下在订票过程中何时需要用Descriptive Programming。

    首先登入系统后,如果需要订票,就要先搜索航班,此时系统要求输入订票乘客的数量,假设我们在第一次录制脚本时选择了1个Passenger,并成功完成订票。然后,我们需要参数化乘客数量来测试订票系统,我们会发现回放会失败。原因在于,不同的乘客的数量导致在订票时需要输入每个乘客的姓名,而录制时,只输入了一个乘客的姓名。而乘客姓名的输入框是随着乘客数量的变化而动态生成的,我们不可能从对象库里得到没有录制的对象,因此必须使用Descriptive Programming。



      

    在录制单个乘客时,我们得到的录制语句是:

    Browser("Welcome: Mercury Tours").Page("Book a Flight: Mercury").WebEdit("passFirst0").Set "Michael"

    Browser("Welcome: Mercury Tours").Page("Book a Flight: Mercury").WebEdit("passLast0").Set "Wang"

    显然WebEdit("passFirst0")和WebEdit("passLast0")是录制时产生的对象并存放到对象库里的。通过对象库,我们可以看到对象的属性如下



      

    系统对于发生多个FirstName时,命名规则是passFirst0,passFirst1…依次类推。因此只要通过简单的Descriptive Programming就可以完成动态FirstName与LastName的识别工作。这里我们假设参数化的乘客数已经赋值给intPassNum,下面是脚本中的关键语句:

    counter = 0

    For i = 0 to (intPassNum)

    Browser("Find a Flight:").Page("Book a Flight:").WebEdit("name:="passFirst""&i).Set "Michael"

    Browser("Find a Flight:").Page("Book a Flight:").WebEdit("name:="passLast""&i).Set "Wang"

    counter = counter + 1

    Next

    3、使用description对象

    使用Description 对象可以返回包含一组Property 对象的Properties 集合对象。Property 对象由属性名和值组成。然后,可以在语句中指定用返回的Properties 集合代替对象名。(每个property 对象都包含一个属性名和值)。

    要创建Properties 集合,可以使用以下语法输入Description.Create 语句:

    Set MyDescription = Description.Create()

    创建Properties 对象(例如,以上示例中的MyDescription)后,就可以输入语句,以便在运行会话期间在Properties 对象中添加、编辑、删除或检索属性和值。这样,就可以在运行会话期间,使用动态方法确定哪个属性以及多少个属性应包含在对象描述中。

    在Properties 集合中填充一组Property 对象(属性和值)后,可以在测试语句中指定用Properties 对象代替对象名。

    例如,假设我们需要完成以下一个操作:

    Window("Error").WinButton("text:=OK", "index:="1"").Click

    我们可以通过Description对象来实现同样的功能,参加下面的代码:

    Set MyDescription = Description.Create()

    MyDescription("text").Value = "OK"

    MyDescription("index").Value = 1

    Window("Error").WinButton(MyDescription).Click

    Set MyDescription = Nothing

    【小结】以上是对QTP中有关处理动态对象中的Descriptive Programming的简单介绍,希望对大家能够有所帮助,就总体而言,如果能够熟练掌握Descriptive Programming,那么有很多实际中的问题就可以迎刃而解。当然Descriptive Programming只是QTP中的一个功能,QTP在实际功能测试中还有很多强大的功能,作为QTP学习的一个系列有机会我会一一介绍。

    Feedback

  • 在QTP中使用描述性编程(实例)

    2009-03-04 17:31:08

    QTP中使用描述性编程是一个提高QTP脚本利用率的很好的方式。
            通常QTP是通过对象库来识别不同的对象,而描述性编程是QTP另外一种能够识别对象的途径,它不依赖于对象库,通过增加一些对象的描述来识别对象的。
     
            说明:本例子是以Flight飞机订票系统的登陆界面为测试页面进行描述的。
     
            步骤一:录制脚本
    Dialog("Login").WinEdit("Agent Name:").Set "Holly"
    Dialog("Login").WinEdit("Password:").SetSecure "46ef0dc7efe5834c73673898279af1204fea51a7"
    Dialog("Login").WinButton("Cancel").Click
    共录制3步操作,输入Agent Name, Password, 点击Cancel按钮
     
            步骤二:初级描述性编程
    Dialog("Regexpwndtitle:=Login").WinEdit("Attached text:=Agent Name:").Set "Holly"
    Dialog("Regexpwndtitle:=Login").WinEdit("Attached text:=Password:").Set “Mercury
    Dialog("Regexpwndtitle:=Login").WinButton("Class Name:=WinButton", "text:=Cancel").Click
            在这里要注意有三点:
            1)如果需要两个以上特性来描述一个对象,需要使用逗号(,)对描述性语言进行分割
            2)使用:=来连接属性和属性值,并且:=两边不能有空格
            3)使用SPY查看对象的属性名和属性值(Tools -> Object Spy)
     
            步骤三:描述性编程提高
    Dim descEditLogin
    Set descEditLogin = Description.Create()
    descEditLogin("Class Name").Value = "Dialog"
    descEditLogin("Regexpwndtitle").Value = "Login"
     
    Dialog(descEditLogin).WinEdit("Attached text:=Agent Name:").Set "Holly"
    Dialog(descEditLogin).WinEdit("Attached text:=Password:").Set "Mercury"
    Dialog(descEditLogin).WinButton("Class Name:=WinButton", "text:=Cancel").Click
            在这里需要注意有两点:
            1)把经常使用到的对象定义为一个对象变量,方便以后调用,减少代码工作量和错误
            2)使用SPY获取对象的属性和属性值
     
            步骤四:使用自定义的环境变量
            在File>>Settings>>Environment中选择user-defined,增加一个变量
    dlgLogin = “Login”
     
            这样脚本可以被修改为:
    Dim descEditLogin
    Set descEditLogin = Description.Create()
    descEditLogin("Class Name").Value = "Dialog"
    descEditLogin("Regexpwndtitle").Value = Environment.Value("dlgLogin")
     
    Dialog(descEditLogin).WinEdit("Attached text:=Agent Name:").Set "Holly"
    Dialog(descEditLogin).WinEdit("Attached text:=Password:").Set "Mercury"
    Dialog(descEditLogin).WinButton("Class Name:=WinButton", "text:=Cancel").Click
            当然,参数化的方式很多,这边介绍的是使用环境变量
     
            步骤五:从XML文件导入环境变量
    <Enviroment>
           <Variable>
                  <Name>dlgLogin</Name>
                  <Value>Login</Value>
           </Variable>
    </Environment>
    可以使用手工导入,也可以使用LoadFromFile自动导入
     
            总结:
            优点是当对象的一些属性变更后,脚本更容易维护。
            比如说对于一个通用对象,比如save, reset, cancel等按钮,一个页面有3个,30个页面就有90个对象,
            假如save变成保存,reset变成重置,cancel变成取消,那么对象库就会产生很大的变动。
            而使用了描述性编程只需要在导入的XML文件中修改一个值就可以了。
            当然描述性编程的作用远远不止这些,这次只是抛砖引玉,希望大家共同进步。
  • QTP关键技术(转)

    2009-03-04 17:26:54

    一 对象识别及存储技术基本常识

    1)测试对象模型(Test Object Model)
            测试对象模型是QTP用来描述应用程序中对象的一组对象类。每个测试对象类拥有一系列用于唯一确定对象属性和一组QTP能够录制的方法
     
    2)测试对象(Test Object)
            用于描述应用程序实际对象的对象,QTP存储这些信息用来在运行时识别和检查对象
     
    3)运行时对象(Run-Time Object)
            是应用程序中的实际对象,对象的方法将在运行中被执行
     
    4)QTP的录制过程
            A.确定用于描述当前操作对象的测试对象类,并创建测试对象
            B.读取当前操作对象属性的当前值,并存储一组属性和属性值到测试对象中
            C.为测试对象创建一个独特的有别于其他对象的名称,通常使用一个突出属性的值
            D.记录在对象上执行的操作
     
    5)QTP的回放过程
            A.根据对象的名称到对象存储库(Object Repository)中查找相应的对象
            B.读取对象的描述,即对象的属性和属性值
            C.基于对象的描述,QTP在应用程序中查找相应的对象
            D.执行相关的操作
     
    二 对Check Point的较为深入理解

    1. 定义:
            将特定属性的当前数据与期望数据进行比较的检查点,用于判定被测试程序功能是否正确
            Check Point可以分两类:QTP内置验证点和自定义验证点
     
    2. QTP内置验证点实现原理及优缺点
            A.录制时,根据用户设置的验证内容,记录数据作为基线数据
            B.回放时,QTP捕获对象运行时的数据,与脚本中的基线数据进行比较
            C.如果基线数据和运行数据相同,结果为PASS,反之为Failed.
            D.优点是 操作简单方便
            E.缺点是 QTP默认的检查的属性有时不符合自己的要求,如希望得到检查的属性没有在里面, 而默认的属性不需要检查等。
     
    3. QTP内置验证点结果的应用
            A.录制的验证点在没有进行调整前,仅仅是给出了检查结果是通过还是错误的
            B.实际的测试过程中,可以根据验证点的结果进行不同的操作
           If Window("Flight Reservation").WinEdit("Name:").Check(CheckPoint("Name:")) = True then
                  msgbox "oh, success!"
    Else
                  msgbox "oh, failure!"
    End If
     
    4. 自定义验证点的应用及优缺点
            A.使用条件语句对实际值和期望值进行对比,然后用Reporter对象报告结果
           '检查Ticket Number
    If CStr(dbTicketNumber) = CStr(DataTable("oTicketNumber", dtLocalSheet)) Then
           Reporter.ReportEvent micPass, "打开订单- TicketNumber", "期望结果是:" & dbTicketNumber & ", 界面显示实际结果是:" & DataTable("oTicketNumber", dtLocalSheet)
    Else
           Reporter.ReportEvent micPass, "打开订单- TicketNumber", "期望结果是:" & dbTicketNumber & ", 界面显示实际结果是:" & DataTable("oTicketNumber", dtLocalSheet)
    End If
            B.优点是 非常灵活,前者实现的所有检查都可以用此方法来实现;
            C.缺点是 代码量大,对测试人员的要求高。
     
    5. 对Check Point的深入理解

            A.个人认为在比较简单的和有Active Screen的情况下可以使用QTP内置的Check Point,在比较复杂的情况下可以通过编程和使用Reporter来完成.
            B.在使用check方法时,必须先在Keyword View或者Active Screen中新建CheckPoint。否则无法对该对象进行check,系统报错说无法在对象仓库中找到此对象。如果插入检查点,系统会自动把相关的对象添加到对象库中。
            我认为检查点并不是一个实实在在的对象。因为你可以对同一个对象设置不同的检查点,可以把它的某个属性既设定成True,也可以设定为False。而对象库中的对象的属性值是必须依赖于对象的实际属性值的。如果随意更改有可能无法识别。还有就是可以针对同一个对象设定多个检查点。在测试窗口中可以看到这两个检查点的名称是区分开来的。所以我认为检查点并不是实际存在的对象,而是一些类似映射的东西。
            尽管检查点并不是对象库中的实在的对象,但是它必须对应到对象库中的某个实实在在的对象,好像它的一个映像一样,而且在实际的操作过程中,QTP还是把它作为一个对象来处理的。
            因为我们无法像其他对象一样把“检查点对象”添加到对象库中,而QTP又认为它是个对象,所以我们无法在专家视图中直接添加检查点脚本。但是我们可以采用编成描述的方式来实现检查点的功能。
            CheckPoint 是一个依赖于Object Repository(对象库)中的某个对象的“虚拟对象”。其具体含义是:如果它所依赖的QTP 对象库中的对象没有了,那么此CheckPoint 也就不存在了;这个“虚拟对象”的属性是从它所依赖的对象的属性中“抽取”出来的,它具有它所依赖的对象的一个或几个属性,但不能增加它所依赖的对象没有的任何属性。
            CheckPoint 是一个“虚拟对象”的重要原因是:每个Object都能在Object Repository找到它的Name、Class Properties,而CheckPoint 在Object Repository中就根本不存在。选择脚本中的某个对象后,在Object Property 的对话框里面有个Respository按钮,点击它后,你会看到此对象在Object Respository 的Name、Class 和 Properties。
            选择一个CheckPoint后,在CheckPoint Properties 的对话框里没有 Respository 按钮,在Object Respository中也找不到此CheckPoint的Name、Class 和 Properties(因为它在对象库中根本就不存在!)。

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

    一 对同步点的理解

      1)QTP的脚本语言是VBScript,脚本在执行的时候,执行语句之间的时间间隔是固定的,也就是说脚本在执行完当前的语句之后,等待固定的时间间隔后开始执行下一条语句
     
      2)问题:假设后一条语句的输入是前一条语句的输出,如果前一条语句还没有执行完,这时候将要导致错误的发生!
     
      3)措施:加入同步点、加入Wait语句

      4)同步点Synchronization Point
      QTP脚本在执行过程中如果遇到同步点,则会暂停脚本的执行,直到对象的属性获取到了预先设定的值,才开始执行下一条脚本。
    如果在规定的时间内没有获取到预先设定的值,则会抛出错误信息。
     
    例如:
      Window("Flight Reservation").ActiveX("Threed Panel Control").WaitProperty "text", "Insert Done...", 10000
      执行到上面这条语句时,QTP会暂停执行,直到显示”Insert Done…”,
      如果在规定的时间10,000ms后text的值没有等于”Insert Done…”,则会抛出错误信息
     
    5)如何获取Synchronization Point
           A.在Recording状态下,通过Insert è Synchronization Point实现
           B.非Recording状态下,在Expert View下,通过Insert è Step Generator è Category(Test Objects)è Object(The Object you’re Testing) è Operation(WaitProperty)è PropertyName、PropertyValue、TimeOut分别填写"text", "Insert Done...", 10000

    6)Wait
      总的来说就是死等,比如说wait 10,当运行到这条语句时,等待10秒钟后,才开始再读下面的语句。所以说写脚本的时候一定要估计好时间,否则的话会浪费运行的时间,或者出现等待时间不足的现象。

    二 并列Action间的参数传递 

      思路:将Action1的输出参数,传递给Action2作为输入参数。
     
      1)创建两个Action,关系是并列关系,不是嵌套的.
               

      2)右键Action1,选Action Properties,在Output Parameters中添加参数OutAction1,点OK
     
      3)右键Action2,选Action Properties,在Input Parameters中添加参数InAction2,点OK
      
      4)将Action1的输出OutAction1,传递给Action2的输入InAction2
     
      右键Action2,选Action Call Properties,弹出Action Call Properties窗口;
      选中InAction2的Value,弹出Value Configuration Options窗口;
      在Parameter中共有四项可供选择,选择Test/Action parameter,
      在Output from previous call(s)中的Action选择Action1,Parameter中选择OutAction1;
      表示Action2中的参数InAction2,是由Action1中的参数OutAction1传递而来。

          

      以上就完成了两个并列Action间参数的传递,Action2只能调用Action1的输出参数,而不能调用Action的输入参数。
     
    三 嵌套Action间的参数传递
      参数传递思路:
      将Action1的输入参数InAction1传递给Action2的输入参数InAction2,
      将Action2的输出参数OutAction2传递给Action1的输出参数OutAction1。
      此文为Hollyzhao于2007年09月在 http://blog.csdn.net/softesting 发布,夜深了,有点累~
      1)创建两个Action,嵌套关系,在关键字视图,拖动Action2到Action1下面有缩进的地方
        
      2)右键Action1,选Action Properties,
      在Input Parameters中添加参数InAction1,
      在Output Parameters中添加参数OutAction1,点OK
     
      3)右键Action2,选Action Properties,
      在Input Parameters中添加参数InAction2,
      在Output Parameters中添加参数OutAction2,点OK
     
      4)在Action1和Action2间建立关联
      右键Action2,选Action Call Properties,弹出Action Call Properties窗口;
      此文为Hollyzhao于2007年09月在 http://blog.csdn.net/softesting 发布,夜深了,有点累~
      选中InAction2的Value,弹出Value Configuration Options窗口;
      在Parameter中共有四项可供选择,选择Test/Action parameter,
      在Parent action parameters的parameter中选择Action1
      同理,OutAction2的Store In值为OutAction1
                 
      以上的操作就是把输入值 通过Action1的输入参数,传递给Action2的输入参数进行使用,
      然后Action2运行后,将输出参数通过Action1的输出参数传递出去。
      这里只是对嵌套Action进行最基本的讲解,在实际使用当中还要灵活运用。
  • QTP取得excel中的单元格值并作比较

    2009-03-03 21:00:19

    1.首先需要了解在QTP中怎么读取Excel中的单元格的脚本如何写?例子如下

            Set xlApp= CreateObject("Excel.Application")

            Set wkBook= xlApp.Workbooks.Open("C:\test.xls")

            Set wkSheet= wkBook.Worksheets("Sheet1")

            j = 2

            For i = 1 To 4

            TestNumberOne=CStr(wkSheet.Cells(j, 1))

            TestNumberTwo= CStr(wkSheet.Cells(j, 2))

            Next

            2.了解如何读取QTP中global表中的单元格的脚本。例子如下:

             DataTable("str_ind_id", dtGlobalSheet)

  • [转]"Cannot identify object" QTP不能识别对象问题的解决方案

    2009-03-02 13:47:07

    "Cannot identify object" QTP不能识别对象问题的解决方案


    QTP疑难问题解答(不能识别对象)

    1.  运行错误:不能识别对象(Cannot identify object)
    出现了这个错误时,就表示当前被测程序的窗口中没有符合条件的对象。
    出现这种错误,可能的原因有以下两种:
    (1) 对象此时在程序中没有显示或不存在。
    (2)在程序中存在该对象,但是它的描述在后来发生了改变,导致与对象仓库中存储的信息不匹配,从而QTP无法识别对象。
    如果你正遇到“Cannot identify object”的问题,解决方法见1.1。
    1.1 如何去识别对象描述(Identifying Test Object Descrīption Problems)
    如果你能在被测程序中看到对象,但仍出现了“Cannot Identify Object”错误信息,这就表示仓库中对象的描述与程序中对象的描述一定存在着差异。
    解决对象描述问题的第一步是找到差异,最简单的方法是将仓库中对象的属性值与程序中对象的属性值进行比较(仓库中对象的属性值可在Object Repository窗口中查看,程序中对象的属性值可用Object Spy功能查看。)
    比较仓库中对象的属性值与程序中对象的属性值的操作见1.3。
    在完成比较后,是否发现存在差异?
    如果是,请参考1.4;
    如果否,请参考1.5。
    1.2 处理对象丢失问题(Solving Missing Object Problems)
    在运行脚本时,QTP尝试进行某种操作,而该操作的对象在程序中却没有出现,出现这种情况的原因有以下几种:
    l           对象不再存在。对象已经被从程序中删除。
    解决方法:见1.2.1。
    l           对象还没有装载。
    解决方法:见1.2.2。
    l           当前的程序页面(或窗口)不正确,不是对象所在的页面(或窗口)。
    解决方法:见1.2.3。
    l           前一个步骤没有正确执行。
    解决方法:见1.2.3。
    1.2.1 解决对象不再存在的问题
    如果对象已经不再在被测程序中存在,则应在脚本中修改或删除相关步骤。
    1.2.2 解决对象装载超时的问题
    如果对象丢失的原因是因为没有足够的时间装载,尝试以下解决方案:
    a.对于Web对象,增加Browser Navigation Timeout(File>Setting>Web页签)时间。
    b.在包括该对象的步骤前使用Wait语句,让QTP在执行该步骤之前等待一段时间。
    1.2.3 检查前面步骤的执行情况
    如果是因为打开了错误的页面(或窗口)导致对象丢失问题,或因为前一步骤执行错误导致对象丢失问题,则请按以下方法检查原因:
    a. 如果怀疑在完成脚本后,被测程序又发生了改变,则检查对象的继承关系以及对象描述。参考1.1。
    b. 如果怀疑脚本错误,则检查脚本的每一个步骤。你可能是遗失了某个步骤,也可能是使用了不正确的方法或参数。
    1.3 测试对象的属性值与Run-time对象的属性值的比较(Comparing Test Object and Run-Time Object Property Values)
    根据以下的步骤比较测试对象与Run-time对象的属性值:
    1)    进入Object Repository窗口(Resources>Object Repository),选择对象。
    2)    用笔记下对象的class以及它的各个属性及属性值。
    3)    打开被测程序,并打开包含被测对象的页面或窗口。
    4)    在QTP中选择菜单Tools>Object Spy或点击Object Spy按钮,打开Object Spy对话框。
    5)    选择“Test Object Properties”选项。
    6)    点击右上角的按钮(带有手图标的),这时QTP窗口以及Object Spy对话框都被最小化。
    7)    在程序页面(或窗口)中点击目标对象,恢复Object Spy对话框,并在对话框中显示对象及其父对象(以树的形式显示),并在Properties页签中显示当前对象的属性及属性值。
    8)    这时Object Spy对话框的Properties页签中显示Run-time对象的所有属性,在此查看对象的class,以及它的属性及属性值,并将它与第2步中记下的内容进行比较。
    1.4 解决对象描述存在的问题(Solving Object Descrīption Problems)
    如果发现仓库中对象的属性值与程序中对象的属性值存在不同,你应该判断这个不同是个别对象的问题,还是其它同类对象也存在相同的问题。
    是所有(或多个)同类对象都存在问题吗?
    如果是,则参考1.6
    如果否,则参考1.7
    1.5 关于父对象描述的识别问题(Identifying Parent Object Descrīption Problems)
    对象的识别还与它所继承的父对象有关。
    如果你能在被测程序中看到对象,而且程序中对象的属性值与仓库中对象的属性值也是一致的,但仍然遭遇到了“Cannot identify object”错误,则这个错误可能与它的父对象有关(如仓库中父对象的属性值与程序中父对象的属性值不一致),也可能是仓库中的对象与程序中对象的继承关系不相同导致的。
    要判断是继承关系中的哪个父对象出现了问题,请尝试下面的方法之一:
    l           重新录制对象,比较新旧对象的父对象。
    欲了解如何完成比较,参考1.8。
    l           对继承关系中的每个父对象,分别插入一个Exist语句,并运行该部分脚本。
    欲了解如果创建Exist语句,参考1.9。
    注:你也可以使用Object Repository窗口中的Highlight in Application功能,在被测程序中定位对象。
    一旦找到了存在问题的父对象,接下来尝试以下方法:
    l           修复存在问题的父对象的描述。方法见1.4。
    l           在专家视图的模式下,查找所有继承存在问题的实例并进行修正。
    举例:1.8.1。
    1.6 解决某类对象的识别问题(Solving Object Identification Problems for a Test Object Class)
    如果你发现某类对象的对象描述对于被测程序来说都不是很合理,或你预期到对象描述中的某属性值是经常变化的,你可以在Object Identification对话框中改变该类的识别属性的设置,或定义该类的Smart Identification设置并启用Smart Identification机制,这样QTP就可以唯一识别对象了。
    1.7    解决单个对象的描述问题(Solving Individual Test Object Descrīption Problems)
    选择下列方法之一来解决对象的描述问题:
    l           如果被测程序中对象描述发生了改变,并且你也清楚改变的内容,并且该改变是永久性或长期性的,你可以直接手工修改仓库中对象描述中的相关属性值。
    l           如果被测程序中的对象的属性值依赖于前面的步骤或其它对象,则将该属性值参数化,这样就可以使用其它步骤的输出值为属性值。
    l           如果属性值的组成部分中,部分是固定的,部分是动态改变的,则可以将属性值设计为正则表达式。
    l           如果属性值是遵循某种规则变化的,或者是不可预期的,则从对象描述中移除该属性,并向对象描述中添加一个或多个属性以便于QTP进行对象识别。
    l           If you can only access the information on the property values during the run session, you can create and use functions that use programmatic Descrīptions to identify the object using property values retrieved earlier in the run session.
    l           如果在录制过程中对象是唯一的,但现在程序中出现了两个或多个描述相同的对象,但是它们在页面(或窗口)中的位置不相同,则应在Object Properties或Object Repository窗口中,向对象描述中添加一个ordinal identifier(index或location)。
    1.8 重新录制对象,以判断父对象描述是否存在问题(Re-recording an Object to Identify Parent Object Descrīption Problems)
    根据下面的指引,来判断父对象的问题。
    通过重新录制对象,来判断它的父对象是否存在问题:
    1) 打开浏览器或程序,来到包含被测对象的页面或窗口。
    2) 在关键字视图模式,选择最后一个组件(component),或在专家视图模式,将光标放在最后一个脚本步骤的下面。
    3) 点击Record按钮,或选择Automation>Record。
    4) 点击(或操作)页面或窗口中的目标对象。
    5) 点击Stop按钮,或选择Automation>Stop,完成步骤添加。
    6) 右击新添加的步骤,并选择Object Properties右键菜单。
    7) 在Object Properties对话框中,点击Repository,打开Object Repository窗口,并选中了新对象。注意记下该对象的继承关系。
    8) 在Object Repository窗口中找到旧的对象(即存在问题的对象),将它的继承关系与新对象的继承关系进行比较。
    通过比较,可以检查到新旧对象是父对象的描述存在不同,还是它们的继承关系根本就不同。
    a. 如果是父对象的描述问题,则修改父对象的描述。参见1.4.
    b. 如果是对象的继承关系不正确(即具有不同的父对象)引起的问题,则在脚本中找到所有使用该继承关系的实例,将它们替换为正确的继承关系。
    举例:参见1.8.1。
    1.8.1 举例
    假设有一个带有Frame的网站,你录制了一个操作针对Image对象“Poster”的操作步骤。在Object Repository窗口,你可以Image对象的继承关系如下所示:
    MyCompany(Browser)
          MyCompany(Page)
              Main(Frame)
                   Poster(image)
    当你运行脚本时,网页看起来没有什么不同,但是识别Image对象时却出现了“Cannot identiry object”的错误提示。当你重新录制操作步骤,然后在Object Repository窗口中查看新的Image对象时,发现该对象的继承关系如下所示:
    MyCompany(Browser)
          MyCompany(Page)
               Poster(image)
    从中可以看出,Frame已经从Web Page中移除,所以尽管Image对象“Poster”的描述没有发生任何改变,但它的继承关系已经改变了。

    在Object Repository窗口,你可以看到新旧Image对象“Poster”是两个完全不同的对象:旧的位于已经被移除的Frame对象之下,而新的则直接位于Page“Mycompany”对象之下。

    修复这个问题,可以在脚本步骤中将旧的对象替换为新的对象。

    为了保持Object Repository窗口的整洁,你应删除那些已不再存在的对象(如本例中的Frame)。

     

    1.9 创建Exist语句,以判断父对象的描述是否存在问题

    QTP在录制发生在某个对象上的操作时,也会同时去了解对象以及它的继承关系。因此,如果在运行过程中,QTP不能识别继承关系中的任何一个父对象,都会导致目标对象的识别失败。

    使用带有Exist方法的Msgbox语句,可以方便的检查出是继承关系中的哪个父对象出现了问题。

    假设在运行下面脚本时,你遇到了“Cannot identify Object”错误信息(但是可以确定对象仓库中Link对象的描述与当前程序中对象的描述是完全一致的):

    Browser("Yahoo!").Page("Yahoo!").Link("Arts & Humanities").Click

    你可以在该语句前面插入以下语句:

    Msgbox Browser("Yahoo!").Exist

    Msgbox Browser("Yahoo!").Page("Yahoo!").Exist

    Msgbox Browser("Yahoo!").Page("Yahoo!").Link("Arts & Humanities").Exist

    然后你从头开始运行脚本,发现第1行语句运行成功,并且弹出信息框,内容为“True”。


     


    但是,当QTP运行到第2步时,就弹出信息框,内容为“False”,表示不能找到Page object。这就证明Page对象存在问题。

    确定了出现问题的父对象以后,修复该问题。参见1.4。

    2.      运行错误:对象不唯一(The object is not unique)

    在被测页面或窗口中的同一父对象下,找到多个与仓库中对象描述相符的Run-time对象。

    请确定是只有个别对象存在此问题,还是此类对象的所有对象(或部分对象)存在此问题。

    此类对象的所有对象(或部分对象)都存在此问题吗?

    如果是,参考2.1;

    如果否,参考2.2。

     

    2.1 为某对象类设置能唯一识别对象的描述规则(Configuring Unique Test Object Descrīptions for a Test Object Class)

    如果你发现某类对象的对象描述都不足以唯一识别对象,你可以在Object Identification对话框中,改变该类的识别属性的设置,或定义该类的Smart Identification设置并启用Smart Identification机制,这样QTP就可以唯一识别对象了。

     

    2.2 设置通唯一识别对象的对象描述(Creating a Unique Test Object Descrīption for an Object)

    查看当前对象的描述,并偿试在程序中找到其它具有相同描述的对象并查看它们的描述。

    l         如果你找到了可以将它们区别开来的一个或多个属性,则修改仓库中对象的相关属性(如向描述中添加属性等),以使QTP在运行时可以唯一识别它。

    l         如果它们的识别属性完全相同,则在对象描述中为对象添加一个ordinal identifier(index或location)。

     

    3.      运行错误:找不到父对象(Parent not found)

    识别对象时,会先识别它的父对象是否正确。如果遇到“Parent not found”的错误信息,请偿试以下解决方法:

    l         修复出现问题的父对象的描述。参见1.4。

    l         在脚本中找到继承关系不正确的实例,并修改为正确的继承关系。例如1.8.1。

     

    4.      运行错误:无效的参数(Invalid arguments)

    一个或多个方法的参数是无效的。

    参数无效可能是参数类型错误,也可能是参数个数不正确。

    根据以下建议,找到方法的正确语法:

    l         在Keyword模式,如果方法包括参数,则在Value栏会显示该方法的参数提示。


    l         在Expert模式,如果方法包括参数,则当你插入方法然后按下Shift+Ctrl+Space键时,QTP会显示该方法的参数的提示信息。(注:只有在Editor Options对话框的General页签中打开了Statement completion功能时才可以看到参数的提示信息。)

     

    5.      运行错误:不能识别item(Cannot identify item)

    QTP不能识别方法参数中指定的列表或树对象的item时,请确认指定的item是否存在于对象之中。

    l         如果item不存在,则删除相关脚本步骤,或修改item。

    l         <SPAN style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-ansi-lang

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

    另外,在QTP9.0中,还可以在对象仓库中更新对象的属性,如果用来识别对象的属性在添加到对象仓库以后被改变了,QTP在回放脚本的过程中就无法识别这一对象,会导致出错。使用“更新应用中对象”这一方法,可以重新定义这些属性,使对象能被识别,所以非常有用。具体操作如下:
    &Oslash;        打开对象仓库管理窗口(Resources...-> Object Repository Manager...)
    &Oslash;        选择要更新的对象
    &Oslash;        选择“更新应用中对象”(Object > Update from Application)或工具栏上的
    &Oslash;        在应用中找到该对象并点击它,选着与对象仓库中相同类的对象

  • QTP检查点设置(转)

    2009-03-01 00:15:00

    检查点类型:

    1.标准检查点(Standard Checkpoint)检查对象的属性,如可以检查某个按钮的是否被选取;
    2.页面检查点(Page Checkpoint) 检查网页的特性,如可以检查加载页面所需的时间,或者检查网页是否包含损坏的链接;
    3.图片检查点(Image Checkpoint)检查应用程序或网页中的图像的值,如可以检查所选图像的源文文件是否正确;
    4.位图检查点(Bitmap Checkpoint)检查位图格式的网页或应用程序区域;如。被测应用程序中有一个按钮,显示<查看文档>其中<号码>会被输入到应用程序中其它位置窗体的四位数字代码替换。可以创建一个文本区域检查点,以确认在该按钮上显示与窗体中的号码相同;
    5.文本检查点(Text Checkpoint)检查网页或窗口中的文字内容是否正确;
    6.文本区域检查点(Text Area Checkpoint)检查网页或窗口中的文字是否在指定的区域显示;
    7.表检查点(Table Checkpoint)检查表内部信息,假设被测试应用程序包含一个表,改表列出了从纽约到旧金山所有可用航班。可以添加一个表检查点,以检查该表中的第一个航班的时间是否正确;
    8.数据库检查点(Database Checkpoint)检查由应用程序访问的数据库到内容,如使用数据库检查点来检查网站上包含航班信息的数据库内容;

    9.Output Value 输出值检查,如输出网页中某一图片的属性(长宽高等);
    10.可访问性检查点(Accessibility Checkpoint)对网站区域属性进行识别,以检查是否符合可访问性规则的要求;
    11.XML检查点(XML Checkpoint) 检查xml文件数据内容;

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

    1) 理解检查点

    QuickTest enables you to add checks to your test or component.

    检查点比较指定属性的当前值与期望值,以判断当前的程序(或站点)功能是否正常。

    当你增加了一个检查点以后,在KeyWord模式下会增加一个CheckPoint,在Expert模式下会增加一条CheckPoint语句。在运行测试时,QTP比较CheckPoint的期望值与当前值,如果结果不匹配,则检查点失败,你可以在TestResults窗口中查看到检查点的结果。

    如果你想获取 checkpoint的返回值(一个布尔值,表示检查成功或失败),你必须在专家模式下将checkpoint 参数两端加上括号。如

    a = browser("MyBrowser").page("MyPage").check (checkPoint("MyProperty"))

     

    2) 向测试脚本中添加检查点

    可以在录制脚本的过程中添加,也可以在修改脚本的过程中添加。

    途径:菜单 Insertcheckpoint、或按钮;在Keywork视图中选择一个STEP,然后选右键菜单Insert Standard Checkpoint;在Active Screan中选择任意一个object,然后选右键菜单Insert Standard Checkpoint

     

    3) checkpoint的种类:

    可以在站点或应用程序中插入不同种类的checkpoint

    l         Standard Checkpoint (标准检查点)

    检查程序或网页object的属性值,检查对象如buttons, radio buttons, combo boxes, lists, 等等。

    标准检查点支持所有的add-in 环境 (see Supported Checkpoints).

    l         Image Checkpoint (图像检查点)

    检查程序或网页中图像的值,如你可以检查图像 的源文件是否正确。

    注:你也可以给图片object插入一个标准检查点,这个标准检查点也是图像检查点。

    l         Bitmap Checkpoint(图片检查点)

    将程序或网页的某个区域作为一个图片,建立检查点。如,某个站点可以展示一个城市的地图,这个地图有控制地图缩放的按钮,你点击1次“zoom in”按钮后,录制下放大了的地图快照。通过建立bitmap 检查点,可以检查“zoom in”功能是否正确。

    l         Table Checkpoint(表检查点)

    检查一个表中的信息。例如,假设被测的程序或网站包括一个table,本table中包含了所有的从New YorkSan Fransisco的有效航班。你可以通过Table检查点来检查第1个航班的时间是否正确。

    注意:你也可以通过为表Object插入标准检查点的方法创建表检查点。

    l         Text Checkpoint(文本检查点)

    检查位于程序或网站某个位置的字符串。例如,假设程序或网站显示“Flight departing from New York to San Francisco”的语句,你可以创建一个文本检查点检查"New York" 是否显示于"Flight departing from"  "to San Francisco"之间。

    所有的add-in环境都支持文本检查点。

    l         Text Area Checkpoint

    l         Accessibility Checkpoint

    l         Page Checkpoint

    l         Database Checkpoint

    检查database的内容。例如,可以使用数据库检查点来检查包含航班信息的数据库的内容。

    所有环境都支持数据库检查点。

    l         XML Checkpoint

    l         下表是QTP中在不同环境下对各类检查点的支持情况。

     

    Web

    Std

    VB

    ActiveX

    Other Object

    Standard

    S

    S

    S

    S

    NA

    Image

    S

    NS

    NS

    NS

    NA

    Table

    S

    NS

    NS

    S

    NA

     

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

    一、录制参数设置
           Tools--web event recording configuration
           Toolsoptionsactive screen
           这两个参数的设置直接影响到QTP录制的精度。有的时候由于录制级别设置的问题,有的界面或某个界面元素可能无法被保存下来,那么你就无法在其上使用任何检查点,边录制边添加检查点的方法就可以避免这个问题.


  • QTP自动化测试实践连载丛书

    2009-02-28 23:15:21

  • QTP中GetROProperty,GetTOProperties,GetTOProperty的区别(转)

    2009-02-28 23:11:59

    一、QTP识别对象的原理(by yabest, http://yabest.net

    QTP里的对象有两个概念,一个是
    Test Object(简称TO),一个是Runtime Object(简称RO)。
    这两个概念从字面上不大好理解,也容易混淆。
    但从实际作用上来看,应该说TO就是是仓库文件里定义的仓库对象,RO是被
    测试软件的实际对象。

    QTP识别对象,一般是要求先在对象仓库文件里定义仓库对象,里面存有实际对象的特征属性的值。
    然后在运行的时候,QTP会根据脚本里的对象名字,在对象仓库里找到对应的仓库对象,
    接着根据仓库对象的特征属性描述,在被测试软件里搜索找到相匹配的实际对象,最后就可以对实际对象进行操作了。

    仓库对象TO一般在录制/编写脚本时加入仓库文件,它不仅可以在录制编写时进行修改,
    也可以在运行过程中进行动态修改,以匹配实际对象。

    和TO、RO相关的几个函数有:

    GetTOProperty():取得仓库对象的某个属性的值
    GetTOProperties():取得仓库对象的所有属性的值
    SetTOProperty():设置仓库对象的某个属性的值

    GetROProperty():取得实际对象的某个属性的值


    理解了TO的含义,你就可以自由的用SetTOProperty()定义TO,以灵活的操作RO

    比如有个测试任务,窗口上有很多待检查的记录,每条记录右边都有一个Check按钮,用来检查各条记录。
    记录个数不定,所以Check按钮个数也就不定,只有一个Edit显示记录个数。
    我们要对每条记录进行检查,也就是要点击每个Check按钮。
    但是Check按钮个数不定,不好录制,而且个数可能也很多(上百个),即使能一一录制,那也很麻烦。

    那我有一个好办法,只录制一个按钮对象,它设有两个特征属性 label=OK, index=0
    然后用下面的脚本,就可以完成测试

    buttonNum = CInt(JavaWindow("Test").JavaEdit("Record Num").GetROProperty("value"))
    For buttonIndex = 0 to buttonNum - 1
       JavaWindow("Test").JavaButton("Check").SetTOProperty("index", buttonIndex)
       JavaWindow("Test").JavaButton("Check").Click
    Next


    或者窗口上有New、Modify、Delete、Check等好几个按钮,要把这几个按钮一一按过去
    我在对象仓库里只设置一个按钮对象AnyButton,label特征属性值填任意值,然后用下面脚本执行测试

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "New")
    JavaWindow("Test").JavaButton("AnyButton").Click

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Modify")
    JavaWindow("Test").JavaButton("AnyButton").Click

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Delete")
    JavaWindow("Test").JavaButton("AnyButton").Click

    JavaWindow("Test").JavaButton("AnyButton").SetTOProperty("label", "Check")
    JavaWindow("Test").JavaButton("AnyButton").Click

    另外,QTP还支持脚本描述的方法来定义和访问对象,即不需要在仓库里定义,也能访问和操作实际对象
    ( Written by yabest,
    http://yabest.net )

    如上面两个任务,可以如下实现

    1. 不需要在仓库里定义Check按钮对象,直接用下面脚本来实现测试

    buttonNum = CInt(JavaWindow("Test").JavaEdit("Record Num").GetROProperty("value"))
    For buttonIndex = 0 to buttonNum - 1
       JavaWindow("Test").JavaButton("label:=Check", "index:="+CStr(buttonIndex)).Click
    Next

    2. 不需要在仓库里定义New、Modify、Delete、Check按钮对象,直接用下面脚本来实现测试

    JavaWindow("Test").JavaButton("label:=New").Click
    JavaWindow("Test").JavaButton("label:=Modify").Click
    JavaWindow("Test").JavaButton("label:=Delete").Click
    JavaWindow("Test").JavaButton("label:=Check").Click



    二、QTP操作对象的原理(by yabest, http://yabest.net

    QTP为用户提供了两种操作对象的接口,一种就是对象的封装接口,另一种是对象的自身接口。

    对象的自身接口是对象控件本身的接口,只要做过软件开发,使用过控件的人应该很清楚。
    对象的封装接口是QTP为对象封装的另一层接口,它是QTP通过调用对象的自身接口来实现的。


    两种接口的脚本书写格式的差别在于:
       自身接口需要在对象名后面加object再加属性名或方法名,
       封装接口就不用在对象名后面加object。

    具体格式如下:
       对实际对象的操作:
       对象.object.自身属性
       对象.object.自身方法()
       对象.GetROProperty("封装属性")
       对象.封装方法()

       对仓库对象的操作:
       对象.GetTOProperty("封装属性")
       对象.GetTOProperties()    ’获取所有封装属性的值
       对象.SetTOProperty("封装属性", "封装属性值")

    比如操作JavaEdit对象,通过QTP封装的封装接口,脚本如下:
       设置JavaEdit的内容:
        JavaDialog("Add NE").JavaEdit("NE Name").Set "NE1"
       读取JavaEdit的内容:
        msgbox JavaDialog("Add NE").JavaEdit("NE Name").GetROProperty("value")

    如果通过JavaEdit的自身接口,脚本如下:
       设置JavaEdit的内容:
        JavaDialog("Add NE").JavaEdit("NE Name").object.setText("NE1")
       读取JavaEdit的内容:
        Msgbox JavaDialog("Add NE").JavaEdit("NE Name").object.getText()

    QTP执行JavaEdit().Set语句时,是通过执行JavaEdit().object.setText()来实现的。
    QTP执行JavaEdit().GetROProperty("value"),是通过执行JavaEdit().object.getText()来实现的。
    JavaEdit对象的封装接口Set()和GetROProperty("value"),是QTP封装JavaEdit对象的自身接口setText()和getText()而得来的。

    对象的封装接口是QTP使用的缺省接口,我们录制出来的脚本都是使用封装接口,大家用的也都是封装接口。
    但是封装接口不如自身接口丰富,因为QTP只是封装了部分常用的自身接口嘛。
    所以我们在需要时,可以绕过封装接口,直接调用对象的自身接口。
    不过有些自身接口不够稳定,在实践中偶尔会出现问题,但是概率很少。
    封装接口有相应功能的话,就尽量用封装接口吧!
    ( Written by yabest,
    http://yabest.net )

    理解了封装接口和自身接口的原理,我们就可以更加灵活的操作对象了。

    但是我们怎么知道对象都有哪些封装接口和自身接口呢?
    其实很简单,用对象查看器(Object Spy)查看对象,在查看窗口里有列出这些接口,包括属性和方法。
    窗口中间有选择栏让你选择Run-time Object或者Test Object,
    当你选择Runtime Object时,它显示的就是对象的自身接口(自身的属性和方法)
    当你选择Test Object时,它显示的就是对象的封装接口(封装的属性和方法)

    (注意:GetROProperty访问的是实际对象的封装接口,GetTOProperty访问的是仓库对象的封装接口,
             两者访问的都是对象的封装接口,即Object Spy窗口里选Test Object时显示的属性。
             不要以为GetROProperty访问的是自身接口,即Object Spy窗口里选Run-time Object时显示的属性。
             QTP里的Test Object/Run-time Object的概念太容易让人混淆了!
             它既用来区分仓库对象和实际对象,又用来区分对象的封装接口和自身接口。



    明白了这些,你还等什么呢?快拿起对象查看器,看看对象都有哪些封装接口和自身接口,肆意的操作它,玩弄它吧!

    比如执行
       JavaDialog("Add NE").JavaEdit("NE Name").object.setVisible(false)
    哈哈,你的JavaEdit对象就当场消失不见了!!!
  • QTP自动化对象模型参考(转载)

    2009-02-28 23:07:09

  • QTP资料整理2

    2009-02-27 22:57:40

    QTP调用外部api

      '例1:运行一个exe文件

      Extern.WinExec "d:\1.exe ", 1

      '例2:Beep

       '它让我的机器在办公室里狂响不止!

      Extern.Declare micLong, "Beep", "kernel32.dll", "Beep", micLong

      Extern.Beep 500

      QTP 获取对话框中的信息

      如果弹出对话框就获取上面提示信息并与表中的信息对比,不统一证明弹出的提示出错,主要用来验证

      if browser("web_name").dialog("dialog_name").exist(1) then'如果不出现=false

       error_message=browser("web_name").dialog("diaglog_name").static("用户密码错误!".getRoproperty("text")

         if error_message<>(datatable.value("error_info"))then

       msgbox(error_message)

       end if

       browser("web_name").dialog("diaglog_name").close

       end if

      这里我总结了两点技巧:

      一是:对于dialog中,虽然提示信息对象名称是"用户密码错误",但如果信息对象名称是“该用户不存在”,不用更改会自动识别,我想主要是录制第一遍时,“用户密码错误”只是让运行时能找到这个控制,而不管它是什么内容,因为在对象仓库中,text不是决定该对象的属性

      二是:如果对于提示信息比较长的,可以用mid(error_message,n,m)取一部份特征提示信息进行验证,这样我想可以节省处理时间,又可以避免长度以及空格等字符的处理

      QTP获取对象属性名称用法:

      GetRoProperty----从应用程序界面上获取对象属性(即,是脚本运行时,获取的对象动态属性值)

      例如:获取对象库中index属性值,似乎只能用GetToProperty,因为应用程序界面上对象没有该属性,只是QTP为识别该对象创立的描述属性;

      GetToproperty----从对象库中描述对象的属性,静态值

      GetToProperties----获取用于标识对象的属性集;对于这个集合,有count等属性方法

      QTP在执行过程中往excel中写入值

      DataTable.GlobalSheet.AddParameter "Name", "liuxuedong"

      取datatable特定行的数据可以这样使用

      DataTable.GetSheet("Action1").GetParameter("test\").ValueByRow(1)
  • QTP资料整理

    2009-02-27 22:46:11

    QTP调用外部api

    } rp+o GdB2P0R1h+`16403
    '1:运行一个exe文件
    Extern.WinExec "d:\1.exe ", 1
    '2:Beep
    '它让我的机器在办公室里狂响不止!
    Extern.Declare micLong, "Beep", "kernel32.dll", "Beep", micLong 
    Extern.Beep 500 

    QTP获取对话框中的信息

    如果弹出对话框就获取上面提示信息并与表中的信息对比,不统一证明弹出的提示出错,主要用来验证

    if browser("web_name").dialog("dialog_name").exist(1) then'如果不出现=false
        error_message=browser("web_name").dialog("diaglog_name").static("用户密码错误!".getRoproperty("text")
       if error_message<>(datatable.value("error_info"))then
            msgbox(error_message)
         end if
        browser("web_name").dialog("diaglog_name").close
     end if
    这里我总结了两点技巧:
    一是:对于dialog中,虽然提示信息对象名称是"用户密码错误",但如果信息对象名称是该用户不存在,不用更改会自动识别,我想主要是录制第一遍时,用户密码错误只是让运行时能找到这个控制,而不管它是什么内容,因为在对象仓库中,text不是决定该对象的属性
       二是:如果对于提示信息比较长的,可以用mid(error_message,n,m)取一部份特征提示信息进行验证,这样我想可以节省处理时间,又可以避免长度以及空格等字符的处理

     

    QTP获取对象属性名称用法:

    GetRoProperty----从应用程序界面上获取对象属性(即,是脚本运行时,获取的对象动态属性值)

    例如:获取对象库中index属性值,似乎只能用GetToProperty,因为应用程序界面上对象没有该属性,只是

    QTP为识别该对象创立的描述属性;

    GetToproperty----从对象库中描述对象的属性,静态值
    GetToProperties----获取用于标识对象的属性集;对于这个集合,有count等属性方法
     QTP在执行过程中往excel中写入值

    DataTable.GlobalSheet.AddParameter "Name", "wangyidonglu"

    datatable特定行的数据可以这样使用
    DataTable.GetSheet("Action1").GetParameter("test").ValueByRow(1)

    QTPexcel中写入内容

     filename="D:\xyb\xueyb\xueyb.xls"

    ' create the Excel object

    Set ExcelObj = CreateObject("Excel.Application")

    ExcelObj.Workbooks.Add

    Set NewSheet = ExcelObj.Sheets.Item(1)

    NewSheet.Name = "Page Information"

    row = 1

    NewSheet.Cells(1,1) = "name"

    NewSheet.Cells(1,2) = "xueyb"

    NewSheet.Columns("A:A").ColumnWidth = 20

    NewSheet.Columns("A:A").Font.Bold = True

    NewSheet.Columns("B:B").ColumnWidth = 60

    NewSheet.Columns("B:B").HorizontalAlignment = -4108 ' xlCenter

    ' save the Excel file

    ExcelObj.ActiveWorkbook.SaveAs filename

    ExcelObj.Quit

    Set ExcelObj = Nothing

     

    QTP等待某个对象出现方法

    Window("Flight Reservation").Static("Static").WaitProperty("visible",true,10000)

    例:

    SystemUtil.Run "E:\Program Files\Mercury Interactive\QuickTest Professional\samples\flight\app\flight4a.exe","","E:\Program Files\Mercury Interactive\QuickTest Professional\samples\flight\app\","open"

    Dialog("Login").Click 177,47

    Dialog("Login").WinEdit("Agent Name:").Set "mercury"

    Dialog("Login").WinEdit("Password:").SetSecure "4886c0abed388164f4f9c00404eef82ad60997ea"

    Dialog("Login").WinButton("OK").Click

    If   Window("Flight Reservation").Static("Static").WaitProperty("visible",true,10000) ==true

       msgbox("flight4A登录成功!")

       else

         msgbox("flight4A登录失败!")

    End If 

    QTP childobject的应用

    childobject可以返回界面上满足条件的对象集合,而且与对象库里是否有这些对象无关,这就可以简化对象库;
    返回的对象集合的count方法可以返回对象个数,这就可以通过下标对单个对象进行操作;在出现index标识对象时
    可以进行运用
    :Set m_WinCheck=Descrīption.Create()
         m_WinCheck("nativeclass").Value="Button"
         set All_WinCheck=Window("").Dialog("").Childobject(m_WinCheck)
         n=All_WinCheck.Count()
        for i=0 to n-1

         All_WinCheck(i).Set "ON"

        next

     

    QTP Action之间的参数传递

    例如:Action1中,有如下代码:
    out_str="This is out_string"
    RunAction "Action2",oneIteration,out_str
    Acton2中,在其step->Action Properties中的,input参数栏,加入out_str,
    msgbox(parameter("out_str")),就能正确显示参数了

    QTP自动截图

    1.      选择菜单栏Tools->Option项,再选择Run选项卡,在Run窗体中选中“Save still image captures to results,且它右边的下拉框中选择“For errors”

     

    2.      CaptureBitmap("D:\1.bmp")

    Browser("XX").Page("XX").CaptureBitmap("D:\1.bmp")

    QTP中判断一个网页是否已经被开启

    Dim descBrowser
    Set descBrowser = Descr
    īption.Create()
    descBrowser("openurl").value = http://www.google.cn/
    Set child =
    desktop.ChildObjects(descBrowser)
    msgbox child.count()
    If child.count()=0 Then
        print "
    网页没有被开启\"
    Else
        For i =1 to child.count()
             child(i-1).close

        Next
    End If

  • 实现参数共享的五种方法(QTP)

    2009-02-27 22:31:37

    QTP中对象的共享是通过对象库共享的,有时候我们需要把一些参数提供给其他脚本使用,那么就需要对参数进行共享,参数的共享方式有很多,以下test文件均包含action1action2action3,其中action2action3设置为reuseable action

    (当然也可以在setting中手工或者导入xml的方式批量设置环境变量)

    action1

    environment.Value("aaa")=10

    environment.Value("bbb")=20

        RunAction "Action2", oneIteration 

        RunAction "Action3", oneIteration

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

    action2

    a=environment.Value("aaa")                     

    b=environment.Value("bbb")

    msgbox 

    msgbox b

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

    output 10,20

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

    action3

    msgbox a

    msgbox b

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

    output nothing 

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

    No。2使用全局变量实现的test

    (缺点是虽然在action1中已经声明定义了abaction2中还得声明和定义否则象action3不能输出)

    action1

    Public a

    a="c"

    Public b

    b="d"

    RunAction "Action2", oneIteration

    RunAction "Action3", oneIteration

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

    action2

    Public a

      a="a"

    Public b

      b="b"

    msgbox a

    msgbox b

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

    output a b

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

    action3

    msgbox a

    msgbox b

    output nothing

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

    No。3使用globalsheet实现的test

    (当使用的变量比较多的时候,这种方式一目了然)

    action1

    RunAction "Action2", oneIteration

    RunAction "Action3", oneIteration

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

    action2

    datatable.SetCurrentRow(2)

    a=datatable.Value("A","Global")

    b=datatable.Value("B","Global")

    msgbox a

    msgbox b

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

    output 10,20

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

    action3

    msgbox a

    msgbox b

    output nothing

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

    No。4input parameteroutput parameter实现参数间的传递

    例如actionAcationB的传递参数,可以用通过设置action propertyinput parameteroutput parameter实现,具体参看相关文档,这种比较适合某两个action之间的参数传递

    No。5repository parameters实现

    qtp9x中,resources->object repositoy Manager->tools->manage repository parameters


    可以把参数和相关的对象库关联起来,具体操作参看帮助文件,这种方法个人认为不太方便,每次使用的时候还得关联相应的参数,不太推荐

  • QTP 常用的函数2

    2009-02-27 15:48:15

    1.GetCellData函数

      作用:获取单元格的值

      例: 

     rowCount = Browser("xxx ").Page("xxx ").Frame("xxx").WebTable("xxx").RowCount
    For counter = 1 To rowCount
      text = Browser("xxx").Page("xxx").Frame("xxx").WebTable("xxx").GetCellData(counter,1)
      If (text = "xxx") Then
        counter = counter - 1
        selectNO = "#" & counter
        Browser("xxx").Page("xxx").Frame("xxx").WebRadioGroup("xxx").Select selectNO
        Exit For
      End If
    Next

      2.把值插入datatable里

      例:

          datatable.setcurrentrow(i)
            datatable.value("name","Global")="name"
            datatable.value("passwd","Global")="passwd"

      3.用代码来启动浏览器

       Browser1 = "IE"
      StartURL = "www.51testing.com"
      IF Browser1 = "IE" THEN
         set IE = CreateObject("InternetExplorer.Application")
         IE.Visible = true
         IE.Navigate StartURL
      END IF

      4.ExecuteFile函数

      作用:ExecuteFile 可以直接执行vbs文件,而不需要将其导入resource中
            ExecuteFile FileName
      说明:where FileName is the absolute or relative path of your VBscrīpt file.

      例:ExecuteFile("F:\test.vbs")

      5.Strcomp函数

      作用:比较文本

      例:

      dim strtext1,strtext2,str ,str1,comp1
         strtext1 = "xxx"
         strtext2 = "xxx"
         str = VbWindow("xxx").VbWindow("xxx").VbLabe1("xxx").GetTOProperty("text")
         str1= VbWindow("xxx").VbWindow("xxx").VbLabel("xxx").GetTOProperty("text")
         comp1=strcomp(strtext1,str,0)
         If  comp=0 Then
              msgbox “这两个串相等”
         else
           msgbox str
         End If

      6.CaptureBitmap

      作用:捕获屏幕

      7. GetROProperty

      作用:取对象属性值

      例:

     VbWindow("xxx").VbWindow("xxx").VbWindow("xxx").ActiveX("xxx").GetROProperty("TextMatrix(1,0)") 

      8.

       ExitAction - 退出当前操作,无论其循环属性如何。
      ExitActionIteration - 退出操作的当前循环。
      ExitRun - 退出测试,无论其循环属性如何。
      ExitGlobalIteration - 退出当前全局循环。

      9.如何使用Excel对象处理数据?

       Dim xl
      打开excel文件
      Function OpenExcelFile(strFilePath)
      Set xl = CreateObject("Excel.Application")
      xl.Workbooks.Open strFilePath
      End Function
      获得指定单元格数据
      Function GetCellData(strSheet,rwIndex,colIndex)
      GetCellData = xl.WorkSheets(strSheet).Cells(rwIndex,colIndex)
      End Function
      填充单元格数据
      Function PutCellData(strSheet,rwIndex,colIndex,varData)
      xl.WorkSheets(strSheet).Cells(rwIndex,colIndex) = varData
      End Function
      保存并推出
      Function SaveAndQuit()
      xl.Activeworkbook.save
      xl.Quit
      Set xl = nothing
      End Function

      10.连接sql数据库

      例:

         Dim res,cmd,sql
         Set Res=createobject("adodb.recordset")
         Set Cmd=createobject("adodb.command")
         Cmd.activeconnection="Provider=SQLOLEDB.1;Password=111111;Persist Security Info=True;User ID=sa;Initial Catalog=xhq;Data Source=192.168.191.142"  '这句话是连接数据库的数据源,要做修改
        Cmd.CommandType = 1
        sql="selec t * from 表 where name=username"
        Cmd.CommandText = sql
        Set res = Cmd.Execute()
        Set res = nothing
        Set cmd.ActiveConnection = nothing
        Set Cmd= nothing

  • QTP

    2009-02-27 10:29:11

     判断对话框是否存在
    if (JavaWindow("nGen FAT - 1.2.10 - COPARN").JavaDialog("ioc0285").exist=true) then
     Reporter.ReportEvent micPass ,"ACA, issame", "eeror"
     End if

     

    If JavaWindow("nGen FAT - 1.2.10").JavaEdit("GuiHTMLPane").Exist Then
     Reporter.ReportEvent micPass, "The grouding movement", "is successfully"
    End if

    判断按钮是否是灰色
     IsEnable =JavaWindow("nGen FAT - 1.2.10 - Gate_2").JavaButton("Release F3").GetROProperty("enabled")
    If IsEnabled=False  then
      reporter.ReportEvent micpass,"Release is", "successfully"
    End if
    (GetROProperty("enabled")中的enabled是个可以判断是否为灰色的对象属性)

    调试这个值的输出:MsgBox
    MsgBox IsEnable =JavaWindow("nGen FAT - 1.2.10 - Gate_2").JavaButton("Release F3").GetROProperty("enabled")

  • QTP通用脚本示例

    2009-02-25 21:05:04

    贴出一点通用脚本' (函数名.................................................行号)
    ' FunName ................................................. 22            函数模型示例
    ' writeRpt..................................................... 41            向QTP报告中添加报告
    ' tableInfo.................................................... 66            输出此表格中单元格中的文字信息
    ' objImage................................................... 99            输出在QTP报告显示对象截图的字符串

    '***********************    函数模型   **************************************
    'Public Function FunName()
    ''函数说明
    'Err.Clear    ' 清除错误
    'On error resume next
    ''从此行开始添加函数代码
    ' If err.Description <> "" Then
    '   FunName = FunName & "FunName函数出错:" & chr(13) _
    '       & "<br>" & "错误代码:" & CStr(Err.Number) & chr(13) _
    '       & "<br>" & "错误描述: " & Err.Description & chr(13) _
    '       & "<br>" & "错误来源:"& Err.Source & chr(13) & "<br>"
    '   reporter.ReportEvent 1,"FunName函数出错",FunName'将错误写入报告中
    ' End If
    'Err.Clear
    'On error goto 0
    ''示例
    'End Function
    '****************************************************************************************

    '************************** 向QTP报告中写入自定义信息 ***********************                                    
    Public Function writeRpt(byval EventStatus,byval ReportStepName,byval Details) '向报告中写入信息
    'EventStatus是报告状态 0为micPass;1为micFail;2为micDone;3为micWarning。
    'ReportStepName是报告中预期步骤的名称(对象名)。
    '报告事件的描述。该字符串将在报告的步骤详细信息框架中显示。
    Err.Clear    ' 清除错误
    On error resume next
    Dim CurrentMode '记录当前状态
    CurrentMode = Reporter.Filter '保存当前状态
    Reporter.Filter = 0 '启用报告模式
    Reporter.ReportEvent EventStatus,ReportStepName,Details '写入报告信息
    Reporter.Filter = CurrentMode '还原报告状态
    If err.Description <> "" Then
       writeRpt = writeRpt & "writeRpt函数出错:" & chr(13) _
           & "<br>" & "错误代码:" & CStr(Err.Number) & chr(13) _
           & "<br>" & "错误描述: " & Err.Description & chr(13) _
           & "<br>" & "错误来源:"& Err.Source & chr(13) & "<br>"
       reporter.ReportEvent 1,"writeRpt函数出错",writeRpt'将错误写入报告中
    End If
    Err.Clear
    On error goto 0
    End Function
    '示例 writeRpt(0,"新增名片","向文件夹新增名片成功!")
    '****************************************************************************************

    '******************** 输出表格中单元格中的信息 **************
    Public Function tableInfo(byref myObj)'输入一个表格对象,输出此表格中单元格中的文字信息
       '依次输出表格中单元格中的信息
       '以便在表单页面使用描述性编程时定位表格
    Err.Clear    ' 清除错误
    On error resume next  
       Dim i,j'循环变量
       If myObj.exist(1) = false Then
        tableInfo = "<br>" & chr(13) & "找不到指定的表格!" & "<br>"
        reporter.ReportEvent 1,"tableInfo函数出错",tableInfo
        Exit function
       End If
       tableInfo = "<br>" & chr(13) '在报告中起换行作用
       For i = 1 to myObj.RowCount step 1
        For j = 1 to myObj.ColumnCount(i) step 1
       tableInfo = tableInfo & myObj.GetCellData (i,j) & ","
        Next
        tableInfo = tableInfo & chr(13) & "<br>"
       Next
    '若存在错误信息   
    If err.Description <> "" Then
       tableInfo = tableInfo & "tableInfo函数出错:" & chr(13) _
           & "<br>" & "错误代码:" & CStr(Err.Number) & chr(13) _
           & "<br>" & "错误描述: " & Err.Description & chr(13) _
           & "<br>" & "错误来源:"& Err.Source & chr(13) & "<br>"
       reporter.ReportEvent 1,"tableInfo函数出错",tableInfo'将错误写入报告中
    End If
    Err.Clear
    On error goto 0
    End Function
    ' 示例   call tableInfo(myTable) '输出myTable中单元格的文字信息
    '***********************************************************************

    '********************* 在报告中显示截取的图片 ***************
    Public Function objImage(byref myObject)'输入一个对象,给此对象截图,并输出在报告中显示此图片的字符串
    'myObject 为需要进行截图的对象   函数返回的是在XML中显示图片的字符串
    Err.Clear    ' 清除错误
    On error resume next
    Dim picName
    picName = Year(date) & Month(date) & Day(date) & Hour(Now) & Minute(Now) & Second(Now)
    picName = picName & "-" & RandomNumber(111,999) & ".png"
    picName = Environment("ResultDir") & "\" & picName
       If myObject.exist(1) = false then
    objImage =chr(13) & "objImage :对象不存在,未执行截图。" & chr(13)
    Exit function'对象不存在则退出函数
    End If
    myObject.CaptureBitmap picName,True
    objImage =chr(13)&"截图如下:<br>" & "--&gt; <br> <img src= " & chr(34) & picName& chr(34) & ">" & chr(13) & "<br>" & picName
    If err.Description <> "" Then
       objImage = objImage & "截图失败:" & chr(13) _
           & "<br>" & "错误代码:" & CStr(Err.Number) & chr(13) _
           & "<br>" & "错误描述: " & Err.Description & chr(13) _
           & "<br>" & "错误来源:"& Err.Source & chr(13) & "<br>"
       reporter.ReportEvent 1,"objImage函数出错",objImage'将错误写入报告中
    End If
    Err.Clear
    On error goto 0
    End Function
    '示例    msgbox objImage(byref myObject)
  • QTP脚本例子汇总

    2009-02-25 20:59:06

    例一:
           以下语句指示 QuickTest 选中 Itinerary 网页上的所有复选框:
           Set MyDescrīption = Descrīption.Create()
           MyDescrīption("html tag").Value = "INPUT"
           MyDescrīption("type").Value = "checkbox"
           Set Checkboxes = Browser("Itinerary").Page("Itinerary").ChildObjects(MyDescrīption)
           NoOfChildObjs = Checkboxes.Count
           For Counter=0 to NoOfChildObjs-1
           Checkboxes(Counter).Set "ON"
           Next

           例二:
           目标如下:
           1.   脚本需要处理成功和失败的用户的登陆
           2.  数据驱动
           设计开发脚本如下:
           第一步录制脚本如下
           Browser("智能变电巡检仪系统 4.0").Page("智能变电巡检仪系统 4.0").WebEdit("txtLoginName").Set "吕巍"
           Browser("智能变电巡检仪系统 4.0").Page("智能变电巡检仪系统 4.0").WebButton("登 录").set ""
           Browser("智能变电巡检仪系统 4.0").Page("Page").Syn
           Browser("智能变电巡检仪系统 4.0").Close

           以上为录制的正确的用户名,下边录制错误的用户名
           Browser("智能变电巡检仪系统 4.0").Page("智能变电巡检仪系统 4.0").WebEdit("txtLoginName").Set "xx"
           Browser("智能变电巡检仪系统 4.0").Page("智能变电巡检仪系统 4.0").WebButton("登 录").set ""

           出现错误提示“该用户不存在”
           第二步 增强脚本
           现在需要设计的是一套脚本驱动所有的测试数据,这样可以驱动所有的用例数据,qtp中提供了datatable。
           但是有数据驱动也要做一个事情就是如何处理错误的用户名和正确的用户名,如何结合起来呢?
           这里我把datatable看作存放测试用例的地方,里面放入测试数据,还放入测试的预期结果。这样我的设计已经出现雏形了。
           我把datatable设计为三列
           username password status
           吕x
           xxx      xxxx    该用户不存在

           第三列放入最后执行的结果,我设计的是空为成功登陆,如果有信息就用实际运行的结果和这一列对比.好了万事俱备只欠东风了
           修改脚本如下:
           Dim iStatus
           Browser("智能变电巡检仪系统 4.0").Page("智能变电巡检仪系统 4.0").WebEdit("txtLoginName").Set DataTable("username", dtLocalSheet)
           Browser("智能变电巡检仪系统 4.0").Page("智能变电巡检仪系统 4.0").WebButton("登 录").set DataTable("password", dtLocalSheet)
           iStatus = DataTable("status", dtLocalSheet)
           If  iStatus="" Then
             Browser("智能变电巡检仪系统 4.0").Close
           End If
           If  iStatus="该用户不存在"   Then
                Reporter.ReportEvent micFail, "登陆", "登陆失败."
           End If
           Browser("智能变电巡检仪系统 4.0").Close

           第三步,设置脚本运行过程
           打开 tests-〉settings
           设置RUN TAB页面下中国IT实验室 Datatable iterations中 Run On all rows
           这样就完成了整个脚本的设计工作,以上通过一个简单例子吧脚本的设计和软件的功能结合起来,达成脚本的设计效果。

361/212>
Open Toolbar