qileilove

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

如何评估测试人员绩效

 每一段时间, 就会有人开始讨论QA的performance要如何评量, 有些人会提出以下的index
  - 计算所找到的Bug个数
  - 在一段时间内所开立的测试个案
  - 所执行的测试个案个数
  - 自动化测试个案个数/ 所有测试个案个数
  - 测试涵盖度
  这些index的缺点, 是缺乏考虑整个环境或是项目的状况, 容易会忽略一些会影响的变量. 作者认为如果没有根据context就来衡量个人的绩效, 是一件愚蠢的事情.
  例如有些狡猾的测试人员, 可能会采取一些策略来达到你的index的标准, 但是却危害了整个团队的质量.举各例子来说: 如果manager说要评量engineer每周所找到的bug数, 并且订定每周的标准是10个bugs. 这时候会发生什么事, 每周engineers会想办法找到10个bugs, 但是对于多找的bugs, 有些engineers可能会考虑放到下周再提报出来, 这样才能确保下周他比较容易达到pass的criteria. 这代表bug report是无法反映实时的状况, 很能是慢一周. 所以你有可能会误解这时候状况不严重, 导致你会因为错误的数据而做出不当的决策.
  为什么会这样呢? 主要是因为有些短视的人, 想要用简单的方法, 去解决困难的问题. 可是这个人绩效问题, 真的是没有简单的公式就可以衡量出来的. 而且有些衡量是很主观的, 并且也外受到一些外在因素的影响, 像是所处的工作环境, 或是使用的工具, 或是你本身的个性, 或是老板是否善于鼓励员工...等等, 这些因素都会让相同的人, 产生不同的结果.
  另一个我常见的问题, 那是订定不切实际的目标. 像是"找出主要的bugs", 试问你如何界定他是主要的bug? 并且主要的bug是否代表就是重要的bug呢?
  Over-promise和under-deliver也是个严重的问题, 没有根据自己的能力来订出适当的目标. 另一个相关的就是, manager给所有人都是相同的pass criteria, 既然每个人的能力不同, 你就必须要给每个人设定不同的标准.
  作者建议测试人员试着要和你的经理, 去学习如何订定SMART的衡量标准. 因为每个人能力不同, 项目环境不同, 没有一体适用的标准. 此外也要记得align managers, product teams或是company的goal. (当然啊, 最后这点是比较争议的, 因为你的career path不一定和公司一样)

posted @ 2014-09-18 10:03 顺其自然EVO 阅读(194) | 评论 (0)编辑 收藏

有效性的QA审核检查单

经常有这么一个问题:QA为什么对过程改进有帮助。答案当然与如何理解、如何管理、如何处理QA与项目之间的关系密不可分。这里有很多议素我们需要好好探讨。其中一个比较实在一点的,就是如何让QA可以在审核之中,发现项目是否遵从标准规程之余,还能否发现可以提高项目效率的机会,并且提出有针对性地建议,让项目可以更有效地达成项目的目标。
  QA审核的内容,通常都可以从审核的检查单之中看到一个端倪。如果检查单里的问题,他们的答案明显与项目的效能无关的,按照这样的检查单审核,就当然不能发现如何帮助项目提高效能。所以我提到QA需要制定“有效性”的检查单。
  问题立刻出现了,就是“什么样的检查单才是有效性的检查单?”本文的目的就是希望解答这个问题。
  让我首先作几个声明:
  1)当我们谈“有效性”审核的时候,我们不是说“遵从性”的审核不好。我希望强调一点:“遵从性”是制度化与成熟程度的基础。没有了遵从性的团队、组织,是不成熟的,过程操作一般是低效的。只有具备了一定程度上的遵从性,过程管理才能开始生效。请大家明白这一点。
  所以我们谈有效性审核,其实是说在遵从性审核的基础上,考虑有效性的问题。这是一个能力水平的提升,而不是一个对标准规程的叛逆。
  2)要提升过程的效能,因为每一个项目团队能力、风格不一样,是需要针对性的。这些针对性,就反映在调整检查单上面。我也遇到过这样的问题:“QA的标准规程里有标准的检查单,我们是否必须者用同样的检查单,才是符合标准的QA审核?”
  我希望说明的就是:在符合标准要求的情况下,进行部分的调整是可以容许的。这个概念,就体现在 CMMI 要求标准规程里提供“适配”,并要求制定适配的准则。我们需要了解这一点。当然这个需要对规程与过程管理具备一定的了解与判断能力。这些知识与经验是需要积累的。
  3)千万不要认为只有一个方法制定检查单。做任何事情,都有很多实际方法、途径可以完成。这里只是一个案例。我希望大家可以看到要考虑什么问题,要如何思维,然后发展自己的观察、分析、判断等能力,这才是最重要的。
  好了,我们就谈谈如何制定“有效性”的检查单吧。我拿了一个案例来说明。这个案例是一个组织在使用的,用来审核“估算”的标准检查单。这里就来说明如何从这个检查单开始,加入“有效性”因素的考虑,来达到一个既满足本来的遵从性审核任务,也可以反映项目的操作是否有效的检查单。
  制定有效性检查单的步骤:
  1)我们要知道,“有效性”就是能够达到目标。如果没有目标,或是QA不清楚目标,有效性是无从谈起的。
  所以QA一定需要知道检查单上面每一个条款,如何与项目的目标相关联。
  2)符合性与有效性既相互关连,也相互独立。如果要过程有效,我们需要建立制度化。制度化的一个条件,就是每一个员工都需要有愿意遵从规程,符合规程的意愿。我们需要用一种大家都了解的,一致的方法,来处理日常的、或是不理想的、特殊的情况。要过程改进,我们就需要有遵从的纪律。
  所以有效性是建立在“遵从、符合”的基础上的。
  另一方面,如果规程制定得不合理,或是缺乏灵活性,来适应不同的项目、技术、产品。比如,无论项目大小,周期长短,都要求用“Wideband Delphi”进行估算。这就不灵活了。短周期的小项目就很不适合了。又或是要求严格复杂的测试用例分析、策划等等,也有同样的问题。那么,项目的过程就是符合,也不会有效。
  所以QA需要具备专业态度与独立精神来处理现实中“符合性”与“有效性”的冲突与一致。
  3)因为过程有效性,就是能够达到目标。那么,审核过程的有效性,就需要判断。所以一定要求审核的人(QA)具备这种判断能力,而不是期待把检查单细化到没有判断能力的人也可以实施。
  举一个例子,如果检查单里的问题是:
  一)估算是否按计划的时间完成? 那么谁都可以判断。但这不是有效性的。因为我们没有考虑不按计划的时间完成是否影响项目的目标。
  二)估算完成的时间是否对项目达成目标造成风险?这个问题才是有效性的。那么,就需要判断了。我们不能够有效地定义:超时两天就可以,三天就不可以。这在大部分的情况底下其实是不可能的。能够回答这类问题的人,需要有判断能力。
  这是为什么我们要求QA不断提高自己的能力的原因。
  谈到QA能力,我在这里先问一下:我们可否识别“子过程的目标”与“审核子过程的目标”?我们需要能够分辨这个。举一个例子:“同级评审”的目标,就是发现缺陷。但是为什么我们要审核“同级评审”这个子过程?那么目标就可以有很多:比如,作为考核的依据;查看项目是否遵从标准规程;评价这个子过程的操作实效;发现项目是否具备实施同级评审的技巧,等等。我们一定要能够分辨这些不同层次的目标。
  4)总结一下:要从事有效性的审核也好,制定有效的规程也好,就需要考虑:
  每一个条款或是步骤是为什么,要解决什么问题? (要解决什么?)
  这个条款或是步骤有什么后果? (有什么后果?)
  这些要解决的问题与实施后果如何影响项目的目标?(对项目的影响与风险)
  这些就是QA需要能够回答的问题。
  QA需要养成一个考虑这些问题的习惯来建立自己回答这些问题的能力。
5)假设我们已经具备这些能力,那么制定有效性的检查单就不难了。第一个考虑的概念,就是“层次”。假设EPG提交了一个检查单,如下:
  检查项包括:
  是否按时进行了二次估算?
  作者是否参与集成测试活动估算?
  作者是否参与各模块开发活动估算?
  开发相关人员是否都参与集成测试活动估算?
  是否会议方式估算?
  详细设计估算是否完整有效?
  编码估算是否完整有效?
  单元测试估算是否完整有效?
  集成测试用例估算是否完整有效?
  集成测试执行估算是否完整有效?
  偏差超过约定的偏差值是否再次估算?
  是否完整填写估算人员姓名及过程数据?
  估算报告是否及时归档?
  我们可以看到,这个单里的条款,它们的重要性不是同一个层次的。这不是一个制定任何规程的好习惯。我们需要知道把同一个层次的条项罗列在同一个层面上。比如:
  是否按时进行了二次估算?
  作者是否参与集成测试活动估算?
  有哪些与这个估算相关的活动?
  作者是否参与各模块开发活动估算?
  开发相关人员是否都参与集成测试活动估算?
  是否会议方式估算?
  有哪些与这个估算相关的绩效?
  详细设计估算是否完整有效?
  编码估算是否完整有效?
  单元测试估算是否完整有效?
  集成测试用例估算是否完整有效?
  集成测试执行估算是否完整有效?
  偏差超过约定的偏差值是否再次估算?
  估算报告是否及时归档?
  层次这个概念在很多地方都有应用。看看我们公司的结构,你就可以体会到层次的普遍应用程度。不同层次的,就不会在同一个级别的领导之下。比如,生产、财务、市场都是在同一个层次上。我们不会把“融资”放在同一个层次,它一定是在财务底下的。
  层次会影响效率。比如我们的检查单,合理的层次,会让员工与QA更明确各个部分的重要性,在开展工作的时候也可以更好地掌握注意力。
  要了解层次,可以这样看:同一个层次的,是比较相对独立,互不影响的。低一个层次的,是它上面层次的一部分,或者说,是会对上面层次的提供贡献。比如:“是否按时进行了二次估算?”和“有哪些与这个估算相关的活动?”是相对独立的。但“作者是否参与各模块开发活动估算?”就是与估算相关的活动之一。这个有点像CMMI的级别定义原则。这些都是帮助有效的理念,并且被广泛应用在过程管理与以外的很多方面的。
  检查单的组织可以考虑条项的层次。
  如果不了解这个“层次”的观念,就请你提问。我们务必交流清楚。
6)下一步就是分辨符合性与有效性。还记得上面提到的三个考虑么:
  每一个条款或是步骤是为什么,要解决什么问题?(要解决什么?)
  这个条款或是步骤有什么后果? (有什么后果?)
  这些要解决的问题与实施后果如何影响项目的目标?(对项目的影响与风险)
  我们要知道,如果问题是:
  是否按时进行了二次估算?
  我们的答案就会是“是”或是“否”。这个问题要解决的,就是:你是否遵从计划?是一个符合性的问题。所以解答这个问题一点都不难。但是如果问题是:
  完成二次估算的时间对项目有什么影响?
  这个问题要知道完成时间对项目有什么影响,要知道估算知否有效。这才是有效性的检查项。回答这个问题就一点都不简单。我们要知道这个完成时间有什么后果。早于计划的要求完成?按计划时间要求完成?超时了?超了一点点?超了很多?还有更厉害的:这个模块/任务是否在关键路径上面?我们要知道所有这些,可能还有其他的,才能作一个判断。做这个判断,就要知道关键因素与它们的后果。比如:
  估算晚了两个星期。但这是一个2 年的项目,而且这个任务可以有5 个星期的灵活性。那么,这样晚了两个星期的作用就不很大。他是不符合。但后果不大。
  如果估算晚了两个星期,项目是2 年的项目,而且任务是在关键路径上。后果就可能让项目延误两星期。是大概2 %的延误。这样的延误,绝大部分客户可能发现(重要),但都不会太计较的(不严重)。
  如果估算晚了两天,任务在关键路径,项目是2个月的项目。这样问题就严重好多了。
  所以如果要作有效性审核,我们需要知道关键因素,以及他们的后果。
  一个步骤的目的,如果单单是看项目是否符合,而不考虑后果的,这样的管理,目的就是在于“限制行为”。限制或是规范行为的坏处,就是引起项目的抗拒。所以“限制行为”的管理很难有好效果的。
  7)那么,我们可以开始把这些关键因素组织一下,为把检查单改变成为有效性的检查单做准备。我们需要把原本的改成符合与有效兼顾,并且增加一些遗漏的关键因素。首先,我们可以把下面的条项:
  是否按时进行了二次估算?
  作者是否参与集成测试活动估算?
  有哪些与这个估算相关的活动?
  作者是否参与各模块开发活动估算?
  开发相关人员是否都参与集成测试活动估算?
  是否会议方式估算?
  有哪些与这个估算相关的绩效?
  详细设计估算是否完整有效?
  编码估算是否完整有效?
  单元测试估算是否完整有效?
  集成测试用例估算是否完整有效?
  集成测试执行估算是否完整有效?
  偏差超过约定的偏差值是否再次估算?
  估算报告是否及时归档?
  小心看一下,用提高效率的观点,考虑每一个条项的后果,改编成有效性的条款,如下:
  估算完成的时间对项目的影响:
  作者乐于承诺估算结果的程度与原因:
  (暂时忽略)
  估算的方法与过程保证了估算的合理性:
  (暂时忽略)
  (暂时忽略)
  估算结果得到维护与使用:(包含了“估算报告是否及时归档?”)
  我们现在加入我提到的三个因素:
  负责任务的人亲自牵头估算
  大家(项目组与客户)对任务范围与内容的认识是一致的。
  方法与参考数据的使用是合理的
  检查单就变成:(斜字代表这个步骤新加进来的。)
  估算完成时间对项目的影响:
  作者乐于承诺估算结果的程度与原因:
  负责任务的人牵头估算
  估算的方法与过程保证了估算的合理性:
  大家对任务范围与内容有一致的认识
  估算方法适合项目要求
  方法的调整是合理的
  估算结果得到维护与使用:
  项目计划使用了估算结果来安排任务
  如果需要,可以把原来的符合性的放回去。可以把它们放在另一个部分,也可以把它们放在适合的层次。比如:
  估算完成得时间对项目的影响:
  ·  是否按时进行了二次估算?
  作者乐于承诺估算结果的程度与原因:
  ·   负责任务的人牵头估算
  估算的方法与过程保证了估算的合理性:
  ·  大家对任务范围与内容有一致的认识
  ·  估算方法适合项目要求
  ·  方法的调整是合理的
  ·  是否会议方式估算?
  ·   作者是否参与各模块开发活动估算?
  ·   开发相关人员是否都参与集成测试活动估算
  ·  (如果用Delphi方法)偏差超过约定的偏差值是否再次估算?
  估算结果得到维护与使用:
  ·   估算结果在计划里项目计划使用了估算结果来安排任务
  ·   估算报告是否及时归档?
  以往估算的绩效:
  ·   详细设计估算是否完整有效?
  ·   编码估算是否完整有效?
  ·   单元测试估算是否完整有效?
  ·   集成测试用例估算是否完整有效?
  ·   集成测试执行估算是否完整有效?
  现在的检查单包含了本来的条项,也包括了从有效性的角度的内容。但有效性方面还是不充分的。所以我们需要每一个条项都问这个问题:
  我如何可以判断这些条项的有效程度?这个也需要我们了解过程的关键因素!
  上面已经提到过估算的完成时间如何影响项目。包括延误的比率、客户的要求、任务是否在关键路径等等。其他的,我就把我考虑的加到相关的条项底下,如下:
  估算完成得时间对项目的影响:
  ·   是否按时进行了二次估算?
  ·   延误程度对比项目的里程碑与周期的影响有多严重
  ·   延误如何对比任务的关键路径宽容时间
  ·   考虑这个任务的关键性与项目对它的依赖程度
  作者乐于承诺估算结果的程度与原因:
  ·  负责任务的人牵头估算
  ·  判断负责人对结果的信心
  ·   查看负责人以前的承诺(按时完成任务)表现
  估算的方法与过程保证了估算的合理性:
  ·  大家对任务范围与内容有一致的认识
  o    在如下面两条罗列的估算活动以及其他情况底下,多方面的干系人的表达是一致的。
  o    如果有开估算会议,讨论覆盖足够的内容与议素,但进行顺利,没有理解不一致的迹象。
  o    (还可以有其他的蛛丝马迹来判断)
  · 估算方法的效能适合项目的目标
  ·  参考与使用的历史数据与对比、调整的合理性
  ·  是否会议方式估算?
  ·  作者是否参与各模块开发活动估算?
  ·  开发相关人员是否都参与集成测试活动估算?
  ·   偏差超过约定的偏差值是否再次估算?
  估算结果得到维护与使用:
  ·  项目计划使用了估算结果来安排任务
  ·  项目明确有哪些活动受这个估算结果影响(可以更好处理将来的延误)
  ·  估算报告是否及时归档?
  以往估算的绩效:
  ·  详细设计估算是否完整有效?
  ·  编码估算是否完整有效?
  ·  单元测试估算是否完整有效?
  ·  集成测试用例估算是否完整有效?
  ·  集成测试执行估算是否完整有效?
  希望大家可以关注到这里检查点之间的关联性,以及我们的步骤如何影响项目的专注。
 8)上面的表就是一个从你们本来使用的检查单,增加了审核有效性的角度而得来的。让我们把这个资料放到一个表格里:
  大家可以补充“检查方法”这一列。大家也可以按这个思路,增、减因素与检查项的内容与细节。其实标准的检查单,也应该可以让大家按情况“调整”。请留意,我不说修改,而说调整,含义是:我们需要按标准的思路,让它更有效,而不是把内容随意变动。我说的明白么?
  这样的表格跟以前的有两个分别:
  语气是开放式的,不鼓励是否地打勾。每填一个条项,都希望QA知道需要明白后果。以后习惯了这样思维,我们就可以不那么关注语气的问题了。我鼓励大家目前还是在意一点比较好。
  内容多了一些有效性的关键因素。这个是可以积累的。我们需要从工作中观察,以需要多讨论,多交流,多看书。一位QA发现一个关键因素之后,不能单单加到自己的检查单里。QA小组最好组织讨论会议,让每一位QA都知道一个关键因素的来龙去脉,原因后果。这样才能让QA团队的经验建立起来。
  也请留意,每一个关键因素都有数个检查项,但对项目来说,同一个关键因素的所有检查项加起来只有一个后果,我们不单单在乎是否打勾,而是要判断项目的表现有什么后果。我们可以按关键因素预设一些后果。比如:超越预期、没有风险、有可承担的风险、有不可承担的风险、不可承担又不可缓解的风险等。项目的操作状态,就是这几个关键因素的执行后果。每一个关键因素的后果,都取决于它下面的检查项。但我以前也说过,我觉得每一项打分,然后加起来,不是一个很好的方法,因为有些条项的重要性,会因为其他的条项内容而改变的。我们可以这样做,然后参考那些分数。只是不要把分数看成决定一切就可以了。
  想象一下,如果我们的QA审核报告都是这样的,大家就会越来越清楚如何提高项目的效率。将来的过程问题,大部分都可以从QA报告的历史资料找到答案。过程改进将会变得非常容易。
  9)不要以为这是唯一的有效性检查单,所以不要把这个看成经典。有效性的检查单可以有很多。比如,我们如果不一定要求从你们本来的检查单开始,我们可以从我提到的三个因素开始。那么,检查单就会不一样。
  我们要能够分辨得细一点,要争取了解细一点的议素。比如,不要把上面的检查单看成固定不变的。但也不要以为什么事情都不能是固定的。有些事情固定了,就不灵活,不能适应不同的情况,效果就不好。有些事情是不会改变的,改了,就没有效果了。如何判断呢?
  基本上:
  因果关系是不会变的。没有了因,就不会有果。
  解决方案,就几乎永远都会有不同的方案,达到相同或是接近的效果。
  要进步,我们就要慢慢掌握这些理念,并且能够把它应用到自己的工作上。

posted @ 2014-09-18 09:56 顺其自然EVO 阅读(471) | 评论 (0)编辑 收藏

使用WebInject测试WebService

本文通过学习WebInject官网教材,然后测试自己开发的WebService。
  首先,我们有一个无敌的HelloWorld服务,这个服务超级简单,相信大家都很熟悉。就不介绍了。
  然后,创建SOAP消息,根据我自己的经验,soap消息就是把webservice的WSDL文件中的输入输出message给soap化了。具体看一个例子:
  WSDLmessage格式:
  <wsdl:messagename="sayHelloRequest">
  <wsdl:partname="parameters"element="ns:sayHello"/>
  </wsdl:message>
  sayHello元素的格式:
  <xs:elementname="sayHello">
  <xs:complexType>
  <xs:sequence>
  <xs:elementminOccurs="0"name="args0"nillable="true"type="xs:string"/>
  </xs:sequence>
  </xs:complexType>
  </xs:element>
  转成soap消息格式就是:
<?xmlversion='1.0'encoding='UTF-8'?>
<soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:q0="http://ws.apache.org/axis2"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<q0:sayHello>
<args0>pengyusong</args0>
</q0:sayHello>
</soapenv:Body>
</soapenv:Envelope>
  可以看到soap消息就是把message中的part解析为soap中的body中内容。
  再然后就是测试用例文件:
<testcasesrepeat="1">
<case
id="1"
description1="WebServicesSample-HelloWorld"
url="http://localhost:8080/axis2/services/HelloWorld?wsdl"
method="post"
posttype="text/xml"
postbody="file=>doGoogleSearch.xml"
verifypositive="\<return>Hello,mynameispengyusong\</return>"
/>
</testcases>
最后在执行这个测试用例:通过GUI方式的话,在config.xml文件中指定我们的测试用例文件即可。然后在GUI界面点击Run就会执行成功。
-------------------------------------------------------
Test:test_google.xml-1
WebServicesSample-GoogleSearchAPI
Verify:"Hello,mynameispengyusong"
PassedXMLParser(contentiswell-formed)
PassedPositiveVerification
PassedHTTPResponseCodeVerification(notinerrorrange)
TESTCASEPASSED
ResponseTime=0.015sec
-------------------------------------------------------
StartTime:WedOct3016:46:342013
TotalRunTime:0.409seconds
TestCasesRun:1
TestCasesPassed:1
TestCasesFailed:0
VerificationsPassed:3
VerificationsFailed:0
AverageResponseTime:0.015seconds
MaxResponseTime:0.015seconds
MinResponseTime:0.015seconds

posted @ 2014-09-18 09:31 顺其自然EVO 阅读(220) | 评论 (0)编辑 收藏

LoadRunner之调用远程负载

原因:
  据经验,每生成一个虚拟用户,需要花费负载生成器大约 2M-3M 的内存空间。通常运行 controller的主机很少用作负载生成器。负载生成器的工作多由其他装有 LR Agent的PC 机来担任。如果负载生成器内存的使用率大于了 70%,负载生成器就会变成系统的瓶颈,导致性能测试成绩下降。这种问题需要添加负载生成器来解决。一台 512M内存的 PC 机大约可以生成 80 个左右的负载,而一台 256M 内存的 PC 机大约可以生成50到 60 个左右的负载。
  实现借用远程加压机:
  所以通常做大量用户的负载时,就需要借用其它的机器来加压。此时借用的加压机需要首先安装LoadRunner的Load Generator这个部分组件。再按照以下操作执行:
  LoadRunner在测试web应用的时候,最常用的是分布式性能测试,也就是说由多个负载发起机向应用服务器发起请求。
  那么LR(loadrunner)是如何做到的呢?
  首先,这要多亏于LR的架构,LR是由controller做测试控制的,scenario做测试场景的控制,Vuser模拟用户和load generator做负载产生。
  这样我们就很容易想到,只要分布的其他负载发起机上有Vuser和load generator就能做分布式测试了。
  对了,LR就是这么做的,它通过MI listener(跨防火墙监听)来达到以上的目的,默认接受数据的端口是54345,默认发送数据的端口是50500。
  第一步,我们要安装LR,这样的教程网上已经很多了我就不详述了。不过要注意一点,LR在win2000上安装后就自动打开了上述的端口,而在winXP上需要手动开启。具体步骤见第二步。
  在负载发起机上我们要安装如下组件
  第二步,我们要启动监听的服务,如下步骤
  启动代理
  
  设置代理
  按Settings,在这个选项卡中我们可以配置一些用户名和密码(如果有需要的话)
  
第三步,设置场景来连接负载发起机
  创建场景
  
  点击load generator图标
  
  点击Add按钮,来增加负载发起机
  
  在Name 处填入IP地址,点击确定
  
  这样我们就配置了一个负载发起机,重复上述步骤我们可以添加多个负载发起机。
  然后,我们测试下能不能连上负载发起机,选中负载发起机,点击Connect
  
  看到Status里出现Ready字样,我们就连上了一个负载发起机。
  按确定退出,会在任务栏上看到如下图标
  

posted @ 2014-09-18 09:30 顺其自然EVO 阅读(4743) | 评论 (0)编辑 收藏

Selenium下载百度音乐并验证

package baidu;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
//import org.openqa.selenium.WebDriver.Navigation;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
public class selenium  {
public static void snapshot(TakesScreenshot drivername, String filename)
{
// this method will take screen shot ,require two parameters ,one is driver name, another is file name
File scrFile = drivername.getScreenshotAs(OutputType.FILE);
// Now you can do whatever you need to do with it, for example copy somewhere
try {
System.out.println("save snapshot path is:E:/"+filename);
FileUtils.copyFile(scrFile, new File("E:\\"+filename));
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Can't save screenshot");
e.printStackTrace();
}
finally
{
System.out.println("screen shot finished");
}
}
public static void main (String [] args) throws InterruptedException
{
String URL="http://61.135.169.105/";
//avoid Chrome warnning message like "unsupported command-line flag --ignore-certificate-errors. "
ChromeOptions options = new ChromeOptions();
options.addArguments("--test-type");
System.setProperty("webdriver.chrome.driver", "D:\\selenium\\chromedriver.exe");
WebDriver driver = new ChromeDriver(options);
driver.get(URL);
//max size the browser
driver.manage().window().maximize();
/*
Navigation navigation = driver.navigate();
navigation.to(URL);*/
Thread.sleep(2000);
snapshot((TakesScreenshot)driver,"open_baidu.png");
//WebElement reg=driver.findElement(By.name("tj_reg"));
//reg.click();
//    WebElement keyWord = driver.findElement(By.id("kw1"));
//find the element
WebElement keyWord = driver.findElement(By.xpath("//input[@id='kw1']"));
keyWord.clear();
//send key words
keyWord.sendKeys("小苹果");
Thread.sleep(3000);
snapshot((TakesScreenshot)driver,"input_keyWord.png");
WebElement submit = driver.findElement(By.id("su1"));
System.out.println(submit.getLocation());
submit.click();
//System.out.println(driver.getWindowHandle());
Thread.sleep(5000);
WebElement se=driver.findElement(By.xpath("//*[@id=\"2\"]/div[1]/div[2]/table/tbody/tr/td[5]/span/a")) ;
Actions action = new Actions(driver);
action.clickAndHold(se);
action.sendKeys(Keys.DOWN);
Thread.sleep(5000);
List<WebElement> elementList = driver.findElements(By.tagName("herf"));
for(WebElement e:elementList)
{
System.out.print("-->"+e.getText());
}
//se.click();
// System.out.println(driver.getPageSource());
//  System.out.println(pageSource);
//WebElement link =driver.findElement(By.xpath(SELENIUM_LINK));
WebElement link =driver.findElement(By.xpath("//*[@id=\"2\"]/div[1]/div[2]/table/tbody/tr/td[5]/span/a"));//By.xpath("//*[@id=\"1\"]/h3/a"));     //*[@id="1"]/h3/a
link.click();
Thread.sleep(5000);
driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[1]);
Thread.sleep(5000);
WebElement down =driver.findElement(By.xpath("//*[@id=\"128\"]"));//<i class="icon btn-icon-download-small"></i>
Thread.sleep(5000);
down.click();
snapshot((TakesScreenshot)driver,"down_m.png");
//get page title
System.out.println(driver.getTitle());
Thread.sleep(5000);
WebElement userName=driver.findElement(By.id("TANGRAM__PSP_8__userName"));
WebElement password=driver.findElement(By.id("TANGRAM__PSP_8__password"));
WebElement login=driver.findElement(By.id("TANGRAM__PSP_8__submit"));
Thread.sleep(5000);
userName.sendKeys("QAtest");//your baidu userName
password.sendKeys("mypassword");//your baidu password
login.submit();
//     navigation.back();
snapshot((TakesScreenshot)driver,"open_bake.png");
System.out.println(driver.getTitle()+"\n"+driver.getCurrentUrl());
Thread.sleep(50000);
File file=new File("C:\\Users\\Young\\Downloads\\小苹果.mp3");
if(file.exists())
{
System.out.println("PASS");
}
else
{
System.out.println("FAIL");
}
driver.quit();
}
}
  如果出现:unsupported command-line flag --ignore-certificate-errors.
  //avoid Chrome warnning message like "unsupported command-line flag --ignore-certificate-errors. "
  ChromeOptions options = new ChromeOptions();
  options.addArguments("--test-type");
  System.setProperty("webdriver.chrome.driver", "D:\\selenium\\chromedriver.exe");
  WebDriver driver = new ChromeDriver(options);
English »
 

posted @ 2014-09-18 09:28 顺其自然EVO 阅读(326) | 评论 (0)编辑 收藏

各种测试方法的问题

在 Exploratory Software Testig 一书中, James Whittaker在第二章中, 提到各种测试方法的不足:
  Defect Preventation
  从开发人员的角度来说, 他们希望藉由 design review, code review, static analysis tool, 和 unit test, 来增加软件的质量.
  但是作者觉得这些方法都有些根本的问题:
  (1) 开发人员通常不是个好的测试人员
  - 开发人员想的是"如何才能实现这个功能", 而测试人员则是从"如何才能攻破这个功能" 来思考.
  - 因此开发人员会有盲点, 需要有另一组人从不同观点来思考
  - 但是不代表开发人员不用作测试, 像是 formatting, data validation 和 error handling 等等都需要及时处理和验证. 等到测试人员发现, 时间会花得很长, 也修正的代价也很高
  (2) 静止状态的程序不能完全代表真的测试目标
  - 有很多错误是和执行环境有关系, 通常在开发环境这些错误不会发生的
  (3) 缺乏客户真正数据
  - 有些错误是和客户真实的数据有关, 或者需要实行一段时间后, 累积效果出现后才会有问题
  - 可是开发人员通常没有这些数据, 并且也没有这么长的测试时间, 所以无法找出这类型的错误
  Defect Detection
  通常分成手动测试和自动化测试两种:
  自动化测试
  - 测试人员不一定会是好的开发人员, 有些人可以, 有些人可能不行.
  - 测试程序也是会有 bug, 一旦出现后, 测试人员需要更多时间来除错, 维护它的正确性和强固性. 所以你要花在测试的时间多, 还是应该花在维护测试程序的时间多?
  - 此外测试程序所在的执行环境, 以及所用的测试数据, 不是客户的数据, 所以效果还是有限. 并且客户可能也没有勇气, 让你在他的 production 执行.
  - Oracle Problem 的问题是最难处理的, 也就是当你执行完测试时, 你无法确认是否真的实行正确. 像是 install 完毕, 甚么叫做 install 成功, 是所有 service 都启动, 是所有档案都复制完毕, 还是所有 registry 写正确. 你可能无法列的出来, spec 也不会写甚么叫做功能运作正确.
  手动测试
  - 也是由人来进行测试, 需要充分发聪明才智, 设计出真实客户环境的数据和使用状况. 尤其是有关 business logic 更是需要人脑介入.
  - 手动测试比自动化测试强的地方, 是因为现实状况有太多不确定的因素, 有太多 scenario, 会导致测试程序员小的情况太多, 错误时都需要人脑介入, 一一来跟踪.
  - 可是手动测试很慢, 无法反复使用, 测试步骤可能不一定有规律, 也不一定都能重复.

posted @ 2014-09-18 09:26 顺其自然EVO 阅读(146) | 评论 (0)编辑 收藏

性能测试URL自动转码

English »
 
最近做性能测试,写了个python程序自动将URL里面的‘%2B’,‘20%’,‘3B'等转换成正常字符,方便查看。
import os,sys;
path = sys.path[0]
os.chdir(path)
encode_list = 'encode_list.txt'
result = path + '\\results'
def get_encode():
encode_file = open(path + '\\'+ encode_list)
encode = dict()
for line in encode_file:
if line!='\n' and len(line) >1:
if line.find('read me') <0:
encode[line[1:].strip()] = line[0]
return encode
def get_files():
files = os.listdir(path)
file_list = list()
for file in files:
if file.endswith('.txt') and file!= encode_list:
file_list.append(file)
return file_list
def relace_url_encode(strPri,dicEncode):
items = dicEncode.items()
for (key,value) in items:
if strPri.find(key):
strPri = strPri.replace(key,value)
return strPri
def create_result():
if not os.path.isdir(result):
os.makedirs(result)
def write_result(filePri,strText):
fp = open(result+'\\'+filePri,'w+')
fp.write(strText)
fp.close()
create_result()
encode = get_encode()
file_list = get_files()
for ff in file_list:
try:
f = open(ff)
text = f.read()
finally:
f.close()
temp = relace_url_encode(text,encode)
temp = temp.replace('&','\n')
write_result(ff,temp)
  下面是文件夹结构:
encode.py的代码贴在上面
  encode_list.txt里面装的是转换对照表,其中文件名是hard code在python程序里面的,最好不要改
  前面的是正常字符,后面的是需要转换的字符
  需要转码的URL形如下面的形式:
selectForm=selectForm&publishId=iphone6SapptR&color=%7B%22colorDisplayText%22%3A%22Grey%22%2C%22colorId%22%3A%22Grey%22%2C%22publishId%22%3A%22iphone6SapptM%22%2C%22modelCode%22%3A%22iphone6SmodelM%22%2C%22available%22%3Atrue%7D&rom=%7B%22capacityDisplayText%22%3A%2216GB%22%2C%22capacityId%22%3A%2216GB%22%2C%22imageFileName%22%3A%22iPhoneX-gold.png%22%2C%22publishId%22%3A%22iphone6SapptM%22%2C%22modelCode%22%3A%22iphone6SmodelM%22%2C%22available%22%3Atrue%7D&locationId=%7B%22locationId%22%3A%22Marina_Bay_Sands_Exhibition_Hall_A%22%2C%22locationDisplayText%22%3A%22Marina%20Bay%20Sands%20Exhibition%20Hall%20A%22%2C%22publishId%22%3A%22iphone6SapptM%22%2C%22available%22%3Atrue%7D&dateId=&timeId=&javax.faces.ViewState=H4sIAAAAAAAAAE1QO0sDQRAeL7n4RGIEK9PZWLhgJ1hoQIOH8YGgCBa6uVuTC3e76z5ydxaBNFrYWGhhIVpY5k%2BIhZ2gpZXYW9u6F0LiBzvMst9jZjs%2FYHMpYKqBmxhp5QdoA8v6Fub28Ofzy8zJewasMowFDHtl7ComHBhVdUFknQVezFdWIcVENGJq3hxLwazLQiQ1RafYJRKtJRSHvltylc%2BoNFnTg6ySEDip%2BFLF7Y%2Fi3Su%2Bz8CQA1npn5OYp8ZRNq2xArtx7HuL2qiPKl19gGkN7VQ
bxFXL12%2BHD3k5H1iGmsosfQYtyJnO5gb9W6YlYCFVx73ZzKScUUIV2ncOfBLtMabmdgXjRKhkkyQSeigYZwGTg%2BR1qsP%2Fj1xBLsBSOV7%2FN7s8hypSI6Lw%2Ffj0275cstL97CYONDF%2B%2BQFvW4dVIi46t8Xxm6%2Br%2FiKcx3%2FTRn8XowEAAA%3D%3D&javax.faces.source=color%3A1&javax.faces.partial.event=change&javax.faces.partial.execute=color%20color%3A1&javax.faces.partial.render=productImage%20rom%20timeId%20dateId%20locationId&javax.faces.behavior.event=change&javax.faces.partial.ajax=true
  我把转码过的结果全部放在result文件夹里面,双击运行,所有的txt文件都会被转码。并且该文件夹随便放在哪里,代码均可以执行。
  转码过后:
selectForm=selectForm
publishId=iphone6SapptR
color={"colorDisplayText":"Grey","colorId":"Grey","publishId":"iphone6SapptM","modelCode":"iphone6SmodelM","available":true}
rom={"capacityDisplayText":"16GB","capacityId":"16GB","imageFileName":"iPhoneX-gold.png","publishId":"iphone6SapptM","modelCode":"iphone6SmodelM","available":true}
locationId={"locationId":"Marina_Bay_Sands_Exhibition_Hall_A","locationDisplayText":"Marina_Bay_Sands_Exhibition_Hall_A","publishId":"iphone6SapptM","available":true}
dateId=
timeId=
javax.faces.ViewState=H4sIAAAAAAAAAE1QO0sDQRAeL7n4RGIEK9PZWLhgJ1hoQIOH8YGgCBa6uVuTC3e76z5ydxaBNFrYWGhhIVpY5k+IhZ2gpZXYW9u6F0LiBzvMst9jZjs/YHMpYKqBmxhp5QdoA8v6Fub28Ofzy8zJewasMowFDHtl7ComHBhVdUFknQVezFdWIcVENGJq3hxLwazLQiQ1RafYJRKtJRSHvltylc+oNFnTg6ySEDip+FLF7Y/i3Su+z8CQA1npn5OYp8ZRNq2xArtx7HuL2qiPKl19gGkN7VQ
bxFXL12+HD3k5H1iGmsosfQYtyJnO5gb9W6YlYCFVx73ZzKScUUIV2ncOfBLtMabmdgXjRKhkkyQSeigYZwGTg+R1qsP/j1xBLsBSOV7/N7s8hypSI6Lw/fj0275cstL97CYONDF++QFvW4dVIi46t8Xxm6+r/iKcx3/TRn8XowEAAA==
javax.faces.source=color:1
javax.faces.partial.event=change
javax.faces.partial.execute=color_color:1
javax.faces.partial.render=productImage_rom_timeId_dateId_locationId
javax.faces.behavior.event=change
javax.faces.partial.ajax=true
  转换后就可以更方便的查找对比,方便测试进行。
  应该还有需要改进的地方,如果测试需要,再做改进。

posted @ 2014-09-18 09:23 顺其自然EVO 阅读(513) | 评论 (0)编辑 收藏

Linux-shell获取天气

Linux中的shell获取天气,本来觉的比较难,原来,真简单,个位数的代码就搞定。
  1获取对应城市天气
  所有天气信息都从中国天气网获取。每一个城市多会对应一个id(比如,北京为101010100,因为本人在银川,所以例子中就用银川的id:101170101),通过id就可以获取对应城市实时天气或者全天天气,还可以获取七天天气。
  1.1shell脚本
  shell脚本代码如下:
#!/bin/sh
weatherDateRoot=http://www.weather.com.cn/data/sk/101170101.html
weatherDataFile=weather.html
wget $weatherDateRoot -O $weatherDataFile > /dev/null 2>&1
sed 's/.*temp":"\([0-9]\{1,2\}\).*/\1/g' $weatherDataFile
  此脚本通过将天气信息获取,然后通过正则匹配到当前温度。
  如果你只用这个脚本,不再进行二次处理,那也太麻烦。我获取天气信息后是显示到终端命令提示符中的,所以需要还要在做处理。
  2终端命令提示符中显示天气
  首先获取对应城市天气,如银川对应的实时天气信息在:
  http://www.weather.com.cn/data/sk/101170101.html
  你先在中国天气网搜索到你想要的城市的天气,网址中会包含城市天气id,将上面的网址中的id替换成你城市的id就可以获取。
  还有全天天气信息:
  http://www.weather.com.cn/data/cityinfo/101170101.html
  不知道中国天气网提供七天天气信息没有?如果有,那么我们也可以通过此方法获取七天天气信息。
  2.1获取天气信息
  对应shell脚本:
#!/bin/sh
allDataUrl=http://www.weather.com.cn/data/cityinfo/101170101.html
allDataFile=/home/snowsolf/shell/weather/allDay.html
dataUrl=http://www.weather.com.cn/data/sk/101170101.html
dataFile=/home/snowsolf/shell/weather/weather.html
wget $dataUrl -O $dataFile > /dev/null 2>&1
wget $allDataUrl -O $allDataFile > /dev/null 2>&1
  2.2定时获取
  通过crontab命令设置定时任务,执行crontab -e命令(如果第一次需要设置默认编辑器),然后在文件末尾添加:
  */30 * * * * /home/snowsolf/shell/weather/weather.sh >> /dev/null
  此行代码设置每30分钟执行一次获取天气的脚本,具体crontab命令其它语法可以google或baidu。
  2.3提取天气
  sed 's/.*temp":"\([0-9]\{1,2\}\).*/\1/g'
  此命令可以从获取的实时天气文件中获取实时天气。
  2.4终端命令提示符中显示
  你可以参考http://www.cnblogs.com/snowsolf/p/3192224.html。这里可以让你的命令提示符更绚丽。
  最后上一幅我的命令提示符图:

posted @ 2014-09-17 10:08 顺其自然EVO 阅读(257) | 评论 (0)编辑 收藏

什么是数据库归档

如果您的日常工作中需要对数据库进行管理,那您肯定已经或即将遭遇这样的困惑:随着业务的蓬勃发展,数据库文件的大小逐渐增大,您需要为在线业务提供越来越大的高性能磁盘容量,但数据库的工作性能却日渐变差。如何解决这样的问题呢?一种新兴的技术——数据库归档也许能够帮您的忙。
  数据库归档技术是一种保持在线数据库规模大体不变却有能够为用户应用提供稳定的数据库性能的方法。其工作原理是,将数据库中不经常使用的数据迁移至近线设备,将长期不使用的数据迁移至文件形式归档。这样,随着应用的需要,数据会在在线、近线和文件文档之间移动,如当应用需要访问很久以前的某些数据,它们的物理位置在近线设备,则会自动移动到在线设备。对用户的应用而言,这些都是透明的,就像所有数据都存放在在线设备一样,不会对数据库应用产生任何影响。
  数据库归档把信息生命周期管理的概念引入到应用程序数据管理中,可以监控、分析和预测数据量的增加,利用在线的数据库随时识别并定位不活动的数据或已经完成的业务交易,把长期不用的数据封装归档,这样就大幅降低了活动数据的规模,数据库等应用程序运行时的效率可以大幅提升。经过归档,即使在应用程序本身已经废弃的时候还能够重新利用其数据,同时保持实时访问已归档数据的能力。
  需要指出的是,数据库归档与文件归档并不相同。按照SNIA(存储网络工业协会)的定义,归档是数据集合的一致性拷贝,通常用以长期持久地保存事务或者应用状态记录。一般情况下,归档通常用以审计和分析的目的,而不是用于应用恢复。归档之后,文件的原件一般会被删除,并且需要通过前台的操作来恢复文件。普通的文件归档只能够对文件进行操作,而且归档后的文件一般不再产生变化。而数据库归档则不同,数据在归档之后仍然存在改变的可能,也随时会变成在线的活动数据。
  现已经被HP公司收购的OuterBay公司就是数据库归档领域的一个著名厂商,其提供的数据库归档产品主要有三种:Relocator产品进行在线数据归档,打包归档产品将数据库归档成为文件(.XSD或者.XML格式),子集拷贝产品为用户提供用于测试的数据库拷贝。OuterBay有两个主要的竞争对手,Princeton Softech和Applimation。前者产品主要针对大型机系统设计,而且产生的文件是专有格式;后者公司规模较小,其产品也可有效识别出数据库中访问频率较低的数据,并将其移出数据库,存入在线的历史数据库中。
  事实上,所有的数据库厂商都提供了类似的数据库归档功能,但目前没有形成商用产品,用户可以使用命令或者编程进行相关操作。数据库归档的概念本身十分简单,把一条记录从生产数据库插入到历史数据库中,然后把该条记录在生产数据库中删除就实现了数据库归档的功能。
  但是在线数据库需要高可靠性、错误处理、审计以及异常处理(如断电、数据库崩溃)等高级功能,这些都只能由专业的数据库归档产品提供。
  值得指出的是,数据库归档对管理员的日常备份工作很有帮助。如果没有进行数据库归档,那么不仅需要备份整个大型的数据库,而且备份窗口要求很长。在进行数据归档之后,由于已归档的数据库部分可以随时进行备份而不会影响在线数据库的应用,这部分数据库一般为长时间不活跃的数据,因此备份工作很容易完成,而在线的数据库部分也因为进行数据库归档后而瘦身,备份数据量减少,备份窗口减小,从而整体减少了需要备份的数据总量。而对数据进行恢复的时候,可以在短时间内首先完成在线数据库恢复,之后在在线数据库工作的同时进行其他数据的恢复工作。

posted @ 2014-09-17 10:07 顺其自然EVO 阅读(342) | 评论 (0)编辑 收藏

Java图片上查找图片算法

 之前用按键精灵写过一些游戏辅助,里面有个函数叫FindPic,就上在屏幕范围查找给定的一张图片,返回查找到的坐标位置。
  现在,Java来实现这个函数类似的功能。
  算法描述:
  屏幕截图,得到图A,(查找的目标图片为图B);
  遍历图A的像素点,根据图B的尺寸,得到图B四个角映射到图A上的四个点;
  得到的四个点与图B的四个角像素点的值比较。如果四个点一样,执行步骤4;否则,回到步骤2继续;
  进一步对比,将映射范围内的全部点与图B全部的点比较。如果全部一样,则说明图片已找到;否则,回到步骤2继续;
  这里,像素之间的比较是通过BufferedImage对象获取每个像素的RGB值来比较的。如下,将BufferedImage转换为int二维数组:
1     /**
2      * 根据BufferedImage获取图片RGB数组
3      * @param bfImage
4      * @return
5      */
6     public static int[][] getImageGRB(BufferedImage bfImage) {
7         int width = bfImage.getWidth();
8         int height = bfImage.getHeight();
9         int[][] result = new int[height][width];
10         for (int h = 0; h < height; h++) {
11             for (int w = 0; w < width; w++) {
12                 //使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
13                 result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
14             }
15         }
16         return result;
17     }
  比较两个像素点的RGB值是否相同,是通过异或操作比较的(据说比==效率更高),如果异或操作后得到的值为0,说明两个像素点的RGB一样,否则不一样。
下面附上算法完整java代码:
1 package com.jebysun.test.imagefind;
2
3 import java.awt.AWTException;
4 import java.awt.Rectangle;
5 import java.awt.Robot;
6 import java.awt.Toolkit;
7 import java.awt.image.BufferedImage;
8 import java.io.File;
9 import java.io.IOException;
10
11 import javax.imageio.ImageIO;
12 /**
13  * 屏幕上查找指定图片
14  * @author Jeby Sun
15  * @date 2014-09-13
16  * @website http://www.jebysun.com
17  */
18 public class ImageFindDemo {
19
20     BufferedImage screenShotImage;    //屏幕截图
21     BufferedImage keyImage;           //查找目标图片
22
23     int scrShotImgWidth;              //屏幕截图宽度
24     int scrShotImgHeight;             //屏幕截图高度
25
26     int keyImgWidth;                  //查找目标图片宽度
27     int keyImgHeight;                 //查找目标图片高度
28
29     int[][] screenShotImageRGBData;   //屏幕截图RGB数据
30     int[][] keyImageRGBData;          //查找目标图片RGB数据
31
32     int[][][] findImgData;            //查找结果,目标图标位于屏幕截图上的坐标数据
33
34
35     public ImageFindDemo(String keyImagePath) {
36         screenShotImage = this.getFullScreenShot();
37         keyImage = this.getBfImageFromPath(keyImagePath);
38         screenShotImageRGBData = this.getImageGRB(screenShotImage);
39         keyImageRGBData = this.getImageGRB(keyImage);
40         scrShotImgWidth = screenShotImage.getWidth();
41         scrShotImgHeight = screenShotImage.getHeight();
42         keyImgWidth = keyImage.getWidth();
43         keyImgHeight = keyImage.getHeight();
44
45         //开始查找
46         this.findImage();
47
48     }
49
50     /**
51      * 全屏截图
52      * @return 返回BufferedImage
53      */
54     public BufferedImage getFullScreenShot() {
55         BufferedImage bfImage = null;
56         int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
57         int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
58         try {
59             Robot robot = new Robot();
60             bfImage = robot.createScreenCapture(new Rectangle(0, 0, width, height));
61         } catch (AWTException e) {
62             e.printStackTrace();
63         }
64         return bfImage;
65     }
66
67     /**
68      * 从本地文件读取目标图片
69      * @param keyImagePath - 图片绝对路径
70      * @return 本地图片的BufferedImage对象
71      */
72     public BufferedImage getBfImageFromPath(String keyImagePath) {
73         BufferedImage bfImage = null;
74         try {
75             bfImage = ImageIO.read(new File(keyImagePath));
76         } catch (IOException e) {
77             e.printStackTrace();
78         }
79         return bfImage;
80     }
81
82     /**
83      * 根据BufferedImage获取图片RGB数组
84      * @param bfImage
85      * @return
86      */
87     public int[][] getImageGRB(BufferedImage bfImage) {
88         int width = bfImage.getWidth();
89         int height = bfImage.getHeight();
90         int[][] result = new int[height][width];
91         for (int h = 0; h < height; h++) {
92             for (int w = 0; w < width; w++) {
93                 //使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
94                 result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
95             }
96         }
97         return result;
98     }
99
100
101     /**
102      * 查找图片
103      */
104     public void findImage() {
105         findImgData = new int[keyImgHeight][keyImgWidth][2];
106         //遍历屏幕截图像素点数据
107         for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) {
108             for(int x=0; x<scrShotImgWidth-keyImgWidth; x++) {
109                 //根据目标图的尺寸,得到目标图四个角映射到屏幕截图上的四个点,
110                 //判断截图上对应的四个点与图B的四个角像素点的值是否相同,
111                 //如果相同就将屏幕截图上映射范围内的所有的点与目标图的所有的点进行比较。
112                 if((keyImageRGBData[0][0]^screenShotImageRGBData[y][x])==0
113                         && (keyImageRGBData[0][keyImgWidth-1]^screenShotImageRGBData[y][x+keyImgWidth-1])==0
114                         && (keyImageRGBData[keyImgHeight-1][keyImgWidth-1]^screenShotImageRGBData[y+keyImgHeight-1][x+keyImgWidth-1])==0
115                         && (keyImageRGBData[keyImgHeight-1][0]^screenShotImageRGBData[y+keyImgHeight-1][x])==0) {
116
117                     boolean isFinded = isMatchAll(y, x);
118                     //如果比较结果完全相同,则说明图片找到,填充查找到的位置坐标数据到查找结果数组。
119                     if(isFinded) {
120                         for(int h=0; h<keyImgHeight; h++) {
121                             for(int w=0; w<keyImgWidth; w++) {
122                                 findImgData[h][w][0] = y+h;
123                                 findImgData[h][w][1] = x+w;
124                             }
125                         }
126                         return;
127                     }
128                 }
129             }
130         }
131     }
132
133     /**
134      * 判断屏幕截图上目标图映射范围内的全部点是否全部和小图的点一一对应。
135      * @param y - 与目标图左上角像素点想匹配的屏幕截图y坐标
136      * @param x - 与目标图左上角像素点想匹配的屏幕截图x坐标
137      * @return
138      */
139     public boolean isMatchAll(int y, int x) {
140         int biggerY = 0;
141         int biggerX = 0;
142         int xor = 0;
143         for(int smallerY=0; smallerY<keyImgHeight; smallerY++) {
144             biggerY = y+smallerY;
145             for(int smallerX=0; smallerX<keyImgWidth; smallerX++) {
146                 biggerX = x+smallerX;
147                 if(biggerY>=scrShotImgHeight || biggerX>=scrShotImgWidth) {
148                     return false;
149                 }
150                 xor = keyImageRGBData[smallerY][smallerX]^screenShotImageRGBData[biggerY][biggerX];
151                 if(xor!=0) {
152                     return false;
153                 }
154             }
155             biggerX = x;
156         }
157         return true;
158     }
159
160     /**
161      * 输出查找到的坐标数据
162      */
163     private void printFindData() {
164         for(int y=0; y<keyImgHeight; y++) {
165             for(int x=0; x<keyImgWidth; x++) {
166                 System.out.print("("+this.findImgData[y][x][0]+", "+this.findImgData[y][x][1]+")");
167             }
168             System.out.println();
169         }
170     }
171
172
173     public static void main(String[] args) {
174         String keyImagePath = "D:/key.png";
175         ImageFindDemo demo = new ImageFindDemo(keyImagePath);
176         demo.printFindData();
177     }
178
179 }
  这种算法是精确比较,只要有一个像素点有差异,就会找不到图片。当然,如果想指定一个比较的精确度,我也有个思路,就是在算法步骤4比较映射范围内全部像素点的时候做个统计,如果90%的点都相同,那就是说精确度是0.9。
  另外,可能还要考虑效率问题,不过,我在我的应用场景中并不太在意效率。如果有朋友看到这篇文章,对这个话题有更好的想法,请留言。

posted @ 2014-09-17 09:52 顺其自然EVO 阅读(723) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 45 46 47 48 49 50 51 52 53 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜