qileilove

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

为什么集成测试比单元测试更重要

 单元测试很棒。在假定一些数据的环境下,能顺利通过测试的系统就可算是一个好系统。

  不过,现在可以直连外部资源的集成测试才让程序更有价值。谁知道那些内容商(供应商,vendor)会做出什么傻事来!

   很多人一直尝试着让测试达到100%的代码覆盖率,这是很棒的想法,但我倒觉得它有些基本概念上的问题。LosTechies, Ryan Svihla 提出了"反模式(anti-pattern", 有个有趣的观点: “多数应用都需要与外部资源交互”。他们有许多不错的论点,比如:

  大多数的单元测试都需一个运行着的数据库、网页或应用服务器。

  确实如此。

  全是同数据和外部系统相关的

  我下面将要证明多数Bug并不来源于程序本身,而是由从外部输入的数据所引起的。

  为什么? 因为通常Bug出现在实际的工作环 境中,我们的程序总会处理不好那些外部系统输入的原始数据,或者程序输出到外部系统中的数据。而"单元测试"或TDD中所强调的是提供一组假定的数据,检 验程序是否能够按照预期的方式运行。也就说整个测试是在一个假定环境下进行的。这就是为什么接口总是如此轻易就成功运行了。在这之上还可以达到自动化,预 测试性,以及可重复的测试。但是仍有很多系统无法解决现实的问题。

  因为这样的测试方式所能解决的仅是软件开发要面对的问题中的一部分。如何才能发现真正的问题?最终还是要让你的软件与它的外部资源连接起来运行才能发现。

  举个略为抽象的例子:

  1) 一些来自外部系统的数据.

  2) 应用程序开始处理这些数据.

  3) 应用程序将处理后的数据发送到外部资源中 (一般是与第1步不同的数据)

  4) 外部数据拿到第3步的数据,在处理后再发送到应用程序.

  5) 再次接收到数据,并加以处理.

  如此反复。

  我们常用mocks/stubs或者类似的程序来产生第1步的数据来进行第2步的测试,而测试第5步时所使用的数据也是使用类似的方式产生的。其中第1步和第4步是不可靠,也是不可预计的,因为你根本不知道外部系统会给你什么数据。

  第1步和第4步是程序在上线环境下要面对的,所以它们才是最需要关注的。

  外部系统都很挑剔(External systems are finicky mean things)

  对于一些e-Commerce系统,或者财务系统,各式的接口,各式的数据在各个系统间流转。

  我们来谈一些高层次的问题:

   1)理想情况下,当外部系统更改了要发送的数据,无论是是格式(format)或模式(schema),你希望会提前知道。这有些一厢情愿了。最近我曾 与一个电子商务系统,外部数据系统的税务信息增加了一栏,同时需要应用程序调整内部逻辑。就是外部数据系统已经开始发送数据了,我们才知道的。

  2)理想情况下,当外部系统声称支持新的API,你应该改变应用程序的内部逻辑,并且发送新数据。最后,你会发现,他们支持新的API,要么在他们的UAT(User Acceptance Test)环境而不在上线(PRODUCTION)的环境,或者只在上线环境中而不在UAT环境中。

  3)你的程序已使用关于国内资产的采购信息很顺畅了,外部系统和程序的配合也很好。然后开始加入一些国际资产信息时,你可能并不能及时地发现数据已完全变了。

  这些都是我曾遇到的场景。我要说就是你在单元测试中根据无法了解到未来面对的环境,只有实际运行时才有办法。更悲哀的是那些在凌晨3点把你叫起来的问题通常都这样产生的。

  如何完善测试规格(How to setup your Specs)

  我做设计也是从规格文档开始的。规格(specification)其实就是另一种测试(well-crafted tests)。我会区分规格中的单元和集成,并写不同的代码。

  所谓“单元”的测试规格是测试内部的业务逻辑,看看有没有把东西都串起来了。就是在测试时提供一些场景,确保程序执行正确的逻辑,输出期望的结果。

  而集成(Integration)的测试规格则和外部资源有关。直接提供一组外部资源数据,以及要返回的数据。这些是集成测试中实际关心的东西。和外部资源的交互才能真正确定程序是否可以正常工作。

  [只译出主要概念,详细内容请阅读原文!]

  原文地址:More Reasons Why Integration Tests Can Be More Important Than Unit Tests

  本文转载自:http://blog.csdn.net/horkychen/article/details/8685473

posted @ 2013-03-19 11:07 顺其自然EVO 阅读(177) | 评论 (0)编辑 收藏

LoadRunner 技巧之检查点

 判断脚本是否执行成功是根据服务器返回的状态来确定的,如果服务器返回的HTTP状态为 200 OK ,那么VuGen 就认为脚本正确地运行了,并且是运行通过的。在绝大多数系统出错时会返回错误页面码? 不会一般系统都会返回一个消息提示框,来提升用户感受。例如,“网站繁忙,请稍后”。其实这个时候网站已经无法正确响应用户请求了,但是VuGen 脚本无法识别,会错误地认为网站还能正确访问,导致分析错误。所以这时需要一种检查点函数帮助验证请求发送出去后,服务器的返回是不是期望的内容,如果不 是,那么就说明服务器返回无法提供正常的服务了。

  以loadrunner 自带WebTours 为例:

  1、在录制之间需要打开菜单Vuser ---> run-time setting ,选择Preferences 选项,勾选“Enable Image and text check”选项。如果不勾选此项,将不会执行打找函数。

  2、下面录制访问首页,切换到Tree 视图,选中首面“welcome to the Web Tours site.”

  当然,你可以设置任何有标志性(验证请求是正确)的内容。

  3、弹出find text 窗口。

  Search for specific Text:需要查询的标准文本,与word 中的查找功能十分相似。

  Search Text by start and end of string:文本检查点函数也同样提供了根据左便捷进行查找的功能,选项参考上一节关联函数。

  Search in:设置在服务器返回的哪部分数据中进行查询。

 Save count:这是文本检查点很特别的功能,它将记录查找内容的出现次数并且存放到一个参数中,这里可以填写一个参数名称来存放计数结果

  Fail if:设置在什么情况下文本检查点函数错误,提供了两个选项:Found、NotFound 。 Found 也就是说如果在服务器返回中存在需要检查的对象,那么文本检查点函数出错;选择NotFound 则相反,如果没有找到对应的内容,那么文本检查点函数出错。

  检查点函数的错误会导致整个脚本运行结果的失败,通过这个功能可以方便地定位脚本运行中的逻辑错误。

  4、添加检查点脚本如下:

Action()
{

    web_reg_find("Search=Body",
        "Text=Welcome to the Web Tours site.",
        LAST);

    web_url("WebTours",
        "URL=http://127.0.0.1:2080/WebTours/",
        "Resource=0",
        "RecContentType=text/html",
        "Referer=",
        "Snapshot=t1.inf",
        "Mode=HTML",
        LAST);

    web_url("favicon.ico",
        "URL=http://127.0.0.1:2080/favicon.ico",
        "Resource=1",
        "Referer=",
        LAST);

return 0;
}

  Web_reg_find 函数

  通过LR11 的(F1 键打开)帮助文档, 搜索 web_find 函数,提示:web_find 已经弃用,推荐使用web_reg_find 函数。

  int web_reg_find(const char *attribute_list, LAST );

Web_reg_find( “Search=Body”,     // 定义查找范围
            “SaveCount=ddd”   // 定义查找计数变量名称
            “Text=aaa”         // 定义查找内容
              LAST);

  图片查找函数

  通过web_inage_check 函数可以检查页面上的图片。

  int web_image_check( const char *CheckName, <List of Attributes>, <"Alt=alt"|| "Src=src">, LAST );

Web_reg_find( “Go2Venus”,     // 函数标题
             “Alt=Venus”   // 图片说明
              LAST);

相关链接:

LoadRunner 技巧之协议分析

LoadRunner 技巧之THML 与 URL两种录制模式分析

LoadRunner 技巧之思考时间设置

LoadRunner 技巧之集合点设置

LoadRunner 技巧之自动关联

LoadRunner 技巧之手动关联与预关联

posted @ 2013-03-19 10:42 顺其自然EVO 阅读(341) | 评论 (0)编辑 收藏

怎样进行数据库性能测试

 前言:

  究竟怎样进行数据库性能测试,数据库性能测试需要做些什么?大多数产品线的RD和QA也比较迷茫,经常过来咨询。

  一般说来,做数据库性能测试需要如下几个步骤:

  1、明确测试目的

  2、设计测试模型 (即压力模型)

  3、准备测试集群环境

  4、准备压力测试工具或者编写压力测试脚本

  5、明确性能指标并加监控

  6、根据2设计的测试模型准备测试数据

  7、测试执行

  8、测试分析

  本文依据以上步骤,模拟测试需求- 多实例多盘数据库的性能对比测试,制定测试方案公布给大家,方便大家了解数据库性能测试。

  多实例多盘数据库性能测试方案

  一、测试目的

  1、对比数据库单实例、多实例的在不同硬件设备上的性能情况。

  2、对比单机单实例和多实例的性能情况

  3、验证网络带宽是否成为flash产品发挥性能优势的瓶颈。

  4、验证flash产品是否随时间存在性能衰减的可能。

  二、测试方法概述

  测试基准工具:smart-slap(mysqlslap),Jmeter模拟若干客户端、若干用户执行指定的SQL语句,访问数据库。同时,通过监控工具监控系统负载、mysql数据库的服务,全面了解数据库系统以及硬件的负载情况。

  测试模型
  测试监控及结果统计
  监控工具统计如下信息,分析性能瓶颈。

  系统负载:

  CPU:CUP_IDLE、CPU_WA、SERVER_LOADAVG
  内存:MEM_URATE、MEM_USED
  网卡:NIC_TOTAL_IN、NIC_TOTAL_OUT、

  数据库负载:

  QPS:COM_READS、COM_WEITES
  主从延迟:SECOND_BEHIND_MASTER
  慢查询:SLOW_QUERIES_PT
  连接数:THREADS_CONNECTED、THREADS_RUNNING

  STL-tools 监控集群负载

  附:性能指标:

  系统负载:

  CPU:CUP_IDLE 、CPU_WA、SERVER_LOADAVG
  内存:MEM_URATE、MEM_USED
  网卡:NIC_TOTAL_IN、NIC_TOTAL_OUT、

  数据库负载:

  QPS:COM_READS、COM_WEITES
  主从延迟:SECOND_BEHIND_MASTER
  慢查询:SLOW_QUERIES_PT
  连接数:THREADS_CONNECTED、THREADS_RUNNING

  三、测试准备

  测试环境准备

  假设将四类IO存储设备,进行单、多实例的性能测试对比。则可以部署测试集群如下:

  测试集群具体搭建:

  2台机器 ——–4主4从
  根据测试更换硬件:
  RAID+SAS:
  RAID+SSD:
  Flash:
  Fusion:

  监控:在每台机器上部署数据库监控脚本monitor,最好有统一平台上调度、管理、分析monitor采集到的数据。

  测试工具准备:

  Smart-slap:

  特点:全量发压力,可得到最大QPS,对比不同集群的最大QPS。分析不同集群的最大QPS.

  Jmeter:

  特点:控制实时压力,分析各集群在指定压力下的性能情况。

  测试思路:

  先采用slap进行对不同集群组合进行同样的sql压力。(压力时间)取得不同集群的最大QPS,进行对比。

  取最大QPS的一定比率(如1/8倍,1/4倍,1/2倍,1倍)作为每秒发送的请求压力进行测试。比较各个集群的负载、数据库性能情况。

  网络瓶颈测试:

  同网段3台压力机器 往一个集群压足够多的IO压力。分析各个硬件的IO。磁盘、CPU比网卡提前到达压力阀值说明网卡不是瓶颈。若网卡IO先达到极限则说明网卡存在瓶颈。

  硬件性能衰减测试

  同样压力测试24小时,比较最初1小时,和最后1小时的 TPS.以及各项性能指标。

 测试数据准备:

  数据库:flashT

  36张表:

  Test1- test36:

  表结构:

  CREATE TABLE `test1` (
  `doc_id` int(10) unsigned NOT NULL default ’0′,
  `main_status` tinyint(3) unsigned NOT NULL default ’0′,
  `sub_status` tinyint(3) unsigned NOT NULL default ’0′,
  `create_time` int(10) unsigned NOT NULL default ’0′,
  cid1` smallint(5) unsigned NOT NULL default ’0′,
  ……………
  PRIMARY KEY (`doc_id`)
  );

  数据量:

  每个表达到5000W 行(大概30G)

  36个表

  说明:具体的测试库表结构、类型

  每张表的测试数据量等都需要根据具体测试目的和测试场景进行设计。

  四、测试步骤

  测试一: 不同集群配置,不同实例的性能对比测试

  一: 数据构造,同时验证各硬件集群基本读、写性能。

  1)4个实例全部开启insert 5000W;初步对比写性能。

  用36个slap并发实例分别往36张表insert,往每个表插入5000W 行数据。

  2)4个实例全部开启,每张表20个select并发select。初步对比读性能。

  用36个slap并发实例子每个实例20个并发select请求分别从36张表中取数据。

  二:对比不同集群执行最常用SQL命令的性能情况

  制定数据库系统最常用的SQL语句,数据库执行这些SQL语句的性能。用slap并发用户数从10,50,100,200。

  三:对比不同集群处理最耗时SQL命令的性能情况

  制定数据库系统最耗时的SQL语句,数据库执行这些SQL语句的性能。用slap并发用户数从10,50,100,200。

  四:模拟事务处理,对比各集群性能情况。

  模拟事物,,用slap并发用户数10,50,100,200,500,800,1000分析不同集群分别在多少并发用户数下,TPS值最大。

  五:分析各集群在线事物处理能力。

  模拟事务处理,根据步骤四得到的最大TPS,设置TPS的一定比率作为每秒事务数,用jemeter测试,并发,10,50,100,200,500,800,1000.分析各个并发各种集群下的响应时间分布和其他各项性能指标。分析TPS情况最好的并发数。

  测试二:网卡瓶颈测试

  步骤一:分析测试一中的各项测试结果的CPU、磁盘、网卡等负载情况。

  如果其他几项比网卡提前到达瓶颈。则说明网卡不会成为瓶颈。相反进入步骤二。此外,如果测试一中的各项测试磁盘,网卡,CPU等均未达到瓶颈。则将测试一中的步骤四,增大并发压力,直到出现负载瓶颈。

  步骤二:调整测试。

  测试三:硬件是否衰减情况。

  步骤:用jmeter 持续测试24小时。

  用测试一中的步骤五得到的最好TPS的并发数作为此次测试的并发数,用Jmeter并发测试24小时,分析第一个小时和最后一个小时的TPS,和响应时间分布。

  (注意每一次测试命令中,涉及查询条件的值随机分布)

  五、总结:

  关于数据库性能测试,只要掌握了压力测试工具。最关键的还是设计出符合业务的测试模型,以及测试完成后的测试分析。通过实践抽象出测试模型,进行自动化,则测试过程可以事半功倍。

posted @ 2013-03-19 10:22 顺其自然EVO 阅读(1151) | 评论 (0)编辑 收藏

如何做好Code Review:思考、方法和实践

 最近被要求做一个关于Code Review的讲演。首先要说明的是,我并不是太擅长开展Code Review的活动。做这个完全是因为答应了别人又不好反悔。不过在做准备的过程中还是有一些感想。

  关于Code Review我所了解到的行业中最著名的是Bill Gates汇报。这是微软软件开发过程中的一个重要环节,因为Bill Gates亲自参加而备受关注,在行业中广为流传。

  Ok,进入正题了。

  我们面临的共同问题:

  1、对于开发周期较长的软件项目,可维护差的代码对项目造成了极大的困扰

  2、结构复杂的,体系不清楚的代码会导致新成员或者外部干系人很难融入。

  3、软件项目代码质量低下,Bug众多并且有关联累积效应。

  以上三个问题是我在以往的Code Review活动中总结出来,可以被影响或者解决的问题。也就是说我的观点是如果没有上述问题,或者在目前的软件产品的规模、开发过程的成熟度还没有达到一定级别时Code Review是可以不做的。当然优秀的开发人员会做自我审查,这是另外一个问题了。

  总而言之,我认为开展Code Review活动的前提是

  1、开发过程和质量控制达到了一定的成熟的。

  Code Review必然与重构相关联。如果开发过程中更本就没有重构这样的活动,那估计Review出来的结果不太容易得到执行。

  其次是各种标准和规范的齐备性,没有规矩是不能成方圆的,如果只有一个命名规范,那可想而知Review的内容不会太深入,效果因此大打折扣。

  2、代码达到一定那个的规模一般来说,功能单一的小型项目的体系结构不会太复杂。不太会出现Bug的累积效应。小型项目完全可以通过daily meeting和Test来保证。

  Code Review并不是一个随便就可以做,或者做了就有好结果的活动。不过无论如何,一旦条件成熟或者资源充足都应该积极的开展Code Review的活动。因为它除了进行质量控制以外,还是一个团队成员之间进行沟通和相互学习的好机会。

  Code Review的内容:

  1、代码的规范性

    a、混乱而散漫的命名,例如使用a、b、c这样的单字母对变量进行无意义的命名

    b、随处可见的magic number或则hard code。软件中const在所难免,不过这些const应该被集中管理起来。而不是可以随意、随处的出现

    c、缺少注释,或者注释不完备甚至错误

  2、代码的结构问题

    a、巨大的类或者巨大的方法

    b、过于复杂的实现

    c、紧耦合

    d、重复的代码

 3、其他问题

    a、缺少验证和异常处理。例如不对数据进行验证,或者不处理异常又或者捕获无法处理的异常

    b、对工具和框架的错误使用。例如有的ORM框架提供非常方便的运行时延迟加载功能,方便倒是方便,滥用却会有性能隐忧

    c、缺少可读性。注释和命名规范是一个问题,不过过深的调用层次都会影响代码可读性。

    d、缺少扩展性。例如在没有DLR的情况下在业务层使用匿名对象。

    e、缺少安全控制

    f、性能隐患。例如在C#中使用了非托管资源而没有进行释放,或者说有过多、过于频繁的I/O访问

  4、测试问题

    a、没有测试或者覆盖度不够

    b、测试代码滞后,在更改逻辑后没有对测试进行同步更新

  以上是一些通常的Code Review的内容了。当然这里再强调一点的就是规范的完备性,和开发过程的成熟度。Code Review是一种相对高级软件开发活动,没有必要一定要执行。不过一旦实施成功将会有巨大的回报。

  在实施过程中还有一些心得:

  1、做好准备。如果对项目很熟的话,应该知道哪些地方会有问题,Code Review是重新审视和从内因上解决这些问题的良机。

  2、不要考虑业务逻辑。完全专注于代码的实现,例如算法的效率,抽象的粒度。整个代码体系结构的平衡性

  3、虚心学习。不要有针对性

  4、在原则和现实之间平衡。不可能所有事情都完美,所以有的时候我们坚持原则,有的时候修改规范。

  5、不要让自己Review自己的代码

  6、总结跟进Review的结果,并且坚持开展这个活动。这才是最重要的

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

CMMI过程改进之路——质量保证误区

 如何提升产品质量在业界是一个永恒的话题,零缺陷是理想化的,永远只能作为目标而不能到达,客户基于市场压力和竞争等方面的考虑,优先考虑的往往是进度,如何定位质量保证(QA)角色、如何平衡进度、质量、成本的关系,是质量保证的核心关键,关于质量保证常存在下列误区:

  (1)组织架构中QA团队误区:

  在不少公司,基于成本考虑,缺少了真正的高素质的质量保证人才,这其实是管理层的意识问题,认为设立QA岗位,人才的招聘、培训、薪酬、福利、工作环境的消耗将是一笔不小的成本支出,根本没有必要,实际上是一个不好的作法。组建QA团队,确实是需要一笔费用,如果是高水平的团队,付出费用更大,但从长远来看,这笔费用肯定可以从将来产品质量的提升、成本的降低、进度的提前、客户满意度提升、市场占有率等方面获取丰富的回报。一个质量管理优秀的公司,所获得的收益远远超过了QA团队的人力成本。

  (2)“进度优先还是质量优先”误区?

  实际上要根据公司的商业目标来确定,如果项目进度是十万火急的,不能按时交货,可能就会遭受严重经济损失或失去生存机会,我想肯定的选择是公司的生存第一,在进度保证的前提下,再考虑质量保证。但在一些业界优秀的公司,进度已经往往作为质量的一部分,进行综合平衡考虑和解决。

  (3)QA团队的“选人”误区:

  曾看到一些公司,为了通过CMMI评估,都成立了QA组织,但是选人的标准也是基于成本考虑,往往是一些刚毕业不久的大学生,由于缺乏足够的开发和项目管理经验,在项目的流程执行中,不能给予项目团队有效的指导,不能识别公司的商业目标的价值,只能机械按照过程体系的要求执行,造成项目团队的不信任,甚至引起严重对抗,从而破坏了和诣、开放的卓越团队文化。在一些跨国大公司,其实QA的标准要求是很高的,要有多年的项目开发管理经验,甚至是曾担任过资深的项目经理优先,只能这样,才能够深该理解公司的商业目标和过程体系,并可以作为导师或顾问有效识别、规避项目风险、指引项目团队前进的方向。

  (4)QA团队的“定位和运作”误区:

  QA团队并不能只作为高高再上的检查者,或者只定位为“警察角色”,更应该是作为服务者的角色出现,QA团队是必须为项目团队服务的,要有一颗“谦虚”的心,要做到能换位思考,要能站在管理层和项目团队的角度来考虑问题,我想这样就会明白QA团队的价值和意义。

  QA团队核心工作是推动过程改进、进行缺陷预防,实现公司商业目标 ,因此QA团队最重要的是要明白流程的价值,由于所有流程是基于一定的环境或条件建立的,对不同项目可能作用都不同,甚至可能不适用,要能够从公司全局出发,根据成本、进度、质量、客户满意度等条件进行判断,给项目团队提供帮助,进行过程裁剪,并提供给管理层高价值的真实项目情况,成为管理层真正的“眼睛和耳朵 ”。只有这样,QA团队才能取得管理层的支持,质量保证意识才能融入并扎根于公司卓越企业文化之中。

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

LoadRunner 技巧之集合点设置

Loadrunner 技巧已经整理3篇了,你个一定疑问,这些知识点,网上随处可见。确实,由于长时间没有使用这个工具,造成我的一些概念开始在大脑中模糊,我只是用这种方式来温习。

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

  在loadrunner的虚拟用户中,术语concurrent(并发)和simultaneous(同时)存在一些区别,concurrent 是指虚拟场景中参于运行的虚拟用户。而simultaneous与集合点(rendzvous point)关系更密切,是指在同一时刻一起执行某个任务的虚拟用户。

  我们来想象一个场景,10名运动员参加长跑比赛,出发点同时起跑,他们是并排奔跑的;跑了N圈之后,因为有体能更强的,有体能稍弱的,他们的队形并排变成了前后。几乎一个跑道就可以供应他们的奔跑(运行),那么其余的9条跑道就是空闲的。

  为了充分的利用跑道,可以将跑道的起点设置一个集合点,当所有运动员跑完一圈后在起跑点集合,然后再同时起跑。

  运动员可以看作是虚拟用户,跑道可以看作是系统资源。设置集合点可以模式更加真实的并发请求,从而增加对系统的负载。

  下面录制一个登录触摸屏版139邮箱的脚本。录制步骤:

  1、打开登录页面

  2、插入集合点

  3、输入用户名密码,点击登录按钮

  4、登录页面加载完成,录制结束

  脚本添加集合点

  当我们在不熟悉脚本的情况下,可以通过世录制操作面板来添加集合点。

  如果你非常熟悉每一段脚本代码的作用,可以在脚本中添加:lr_rendezvous 集合点函数。

  录制代码如下:

Action()
{

    web_url("wapmail.10086.cn",
        "URL=http://wapmail.10086.cn/",
        "Resource=0",
        "RecContentType=text/html",
        "Referer=",
        "Snapshot=t19.inf",
        "Mode=HTML",
        EXTRARES,
        "Url=http://wapmail.10086.cn:8000/img/p/logo.jpg", ENDITEM,
        "Url=/favicon.ico", "Referer=", ENDITEM,
        LAST);

    web_custom_request("urs.asmx",
        "URL=https://urs.microsoft.com/urs.asmx?MSURS-Client-Key=FcIacsb4XHR0aOJzGG/quQ%3d%3d&MSURS-Patented-Lock=NGkcbdtnuEs%3d",
        "Method=POST",
        "Resource=0",
        "RecContentType=text/xml",
        "Referer=",
        "Snapshot=t20.inf",
        "Mode=HTML",
        "EncType=text/xml; charset=utf-8",
        "Body=<RepLookup v=\"3\"><G>ED8654D5-B9F0-4DD9-B3E8-F8F560086FDF</G><O>F03F2D77-79E1-4DEC-BBF8-81A5C0790160</O><D>9.0.8110.0</D><C>9.00.8112.16421</C><OS>6.1.7601.1.0</OS><I>9.0.8112.16421</I><L>zh-CN</L><R><Rq><URL>aHR0cDovL3dhcG1haWwuMTAwODYuY24v</URL><O>POST</O><T>TOP</T><HIP>218.204.255.90</HIP></Rq><Rq><URL>aHR0cDovL3dhcG1haWwuMTAwODYuY24vaW5kZXguaHRt</URL><O>POST</O><T>ACTION</T><HIP>218.204.255.90</HIP></Rq></R></RepLookup>",
        LAST);

    web_custom_request("urs.asmx_2",
        "URL=https://urs.microsoft.com/urs.asmx?MSURS-Client-Key=C84C0w6qif5yiuTi%2bfaoMg%3d%3d&MSURS-Patented-Lock=EQjsDl4IFSQ%3d",
        "Method=POST",
        "Resource=0",
        "RecContentType=text/xml",
        "Referer=",
        "Snapshot=t21.inf",
        "Mode=HTML",
        "EncType=text/xml; charset=utf-8",
        "Body=<RepLookup v=\"3\"><G>ED8654D5-B9F0-4DD9-B3E8-F8F560086FDF</G><O>F03F2D77-79E1-4DEC-BBF8-81A5C0790160</O><D>9.0.8110.0</D><C>9.00.8112.16421</C><OS>6.1.7601.1.0</OS><I>9.0.8112.16421</I><L>zh-CN</L><R><Rq><URL>aHR0cDovL3dhcG1haWwuMTAwODYuY24v</URL><O>PRE</O><T>TOP</T><HIP>218.204.255.90</HIP></Rq></R></RepLookup>",
        LAST);

    lr_start_transaction("登陆");  //添加事务

    lr_rendezvous("集合点");   //添加集合点

    web_submit_data("index.htm",
        "Action=http://wapmail.10086.cn/index.htm",
        "Method=POST",
        "Referer=http://wapmail.10086.cn/",
        "Mode=HTML",
        ITEMDATA,
        "Name=ur", "Value=fnngj", ENDITEM, 
        "Name=pw", "Value=heng198876", ENDITEM,
        "Name=apc", "Value=0", ENDITEM,
        "Name=_swv", "Value=5", ENDITEM,
        "Name=a", "Value=3,5 ", ENDITEM,
        "Name=_fv", "Value=3", ENDITEM,
        "Name=clt", "Value=5", ENDITEM,
        LAST);

    web_submit_data("index.htm_2",
        "Action=http://wapmail.10086.cn/index.htm",
        "Method=POST",
        "RecContentType=text/html",
        "Referer=http://wapmail.10086.cn/",
        "Snapshot=t22.inf",
        "Mode=HTML",
        ITEMDATA,
        "Name=ur", "Value=XXXXX", ENDITEM,   //登陆用户名
        "Name=pw", "Value=ooooo", ENDITEM, //登陆密码
        "Name=apc", "Value=0", ENDITEM,
        "Name=_swv", "Value=5", ENDITEM,
        "Name=a", "Value=3,5 ", ENDITEM,
        "Name=_fv", "Value=3", ENDITEM,
        "Name=clt", "Value=5", ENDITEM,
        LAST);

    web_custom_request("handler",
        "URL=http://m.mail.10086.cn/wp1/w3/handler",
        "Method=POST",
        "Resource=0",
        "RecContentType=text/html",
        "Referer=http://m.mail.10086.cn/bv1/home.html?mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&logintype=0&cv=3&swv=5&cli=5",
        "Snapshot=t23.inf",
        "Mode=HTML",
        "EncType=application/x-www-form-urlencoded;charset=UTF-8",
        "Body=&mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&vid=&__randomNumber=1362403651558",
        EXTRARES,
        "Url=/bv1/css/public.css?vn=288", "Referer=http://m.mail.10086.cn/bv1/home.html?mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&logintype=0&cv=3&swv=5&cli=5", ENDITEM,
        "Url=/bv1/js/home.js?vn=288", "Referer=http://m.mail.10086.cn/bv1/home.html?mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&logintype=0&cv=3&swv=5&cli=5", ENDITEM,
        LAST);


    web_custom_request("costanalysis",
        "URL=http://m.mail.10086.cn/wp1/w3/costanalysis",
        "Method=POST",
        "Resource=0",
        "RecContentType=text/html",
        "Referer=http://m.mail.10086.cn/bv1/home.html?mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&logintype=0&cv=3&swv=5&cli=5",
        "Snapshot=t24.inf",
        "Mode=HTML",
        "EncType=application/x-www-form-urlencoded;charset=UTF-8",
        "Body=&mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&vid=&cmd=999&d=478&b=1&t=710&body=2&r=5&ajaxInitTime=419&ajaxCostTime=285&ajaxBeginTime=0&downBeginTime=156&initBeginTime=704&logintime=5100&homejstime=2&homeutiljstime=2&ajaxSendTime=1362403651558&redirectTime=-1&__randomNumber=1362403652268",
        EXTRARES,
        "Url=/bv1/img/global_24.png", "Referer=http://m.mail.10086.cn/bv1/home.html?mo=U0j5GuvVaLG3Xz0qMibbQok8g34_OABo&vn=288&logintype=0&cv=3&swv=5&cli=5", ENDITEM,
        "Url=/favicon.ico", "Referer=", ENDITEM,
        LAST);

    web_custom_request("urs.asmx_3",
        "URL=https://urs.microsoft.com/urs.asmx?MSURS-Client-Key=maVKmMoyQiei4%2bdFLSDDAA%3d%3d&MSURS-Patented-Lock=HitWNt%2b1Bns%3d",
        "Method=POST",
        "Resource=0",
        "RecContentType=text/xml",
        "Referer=",
        "Snapshot=t25.inf",
        "Mode=HTML",
        "EncType=text/xml; charset=utf-8",
        "Body=<RepLookup v=\"3\"><G>ED8654D5-B9F0-4DD9-B3E8-F8F560086FDF</G><O>F03F2D77-79E1-4DEC-BBF8-81A5C0790160</O><D>9.0.8110.0</D><C>9.00.8112.16421</C><OS>6.1.7601.1.0</OS><I>9.0.8112.16421</I><L>zh-CN</L><R><Rq><URL>aHR0cDovL20ubWFpbC4xMDA4Ni5jbi9idjEvaG9tZS5odG1sP21vPVUwajVHdXZWYUxHM1h6MHFNaWJiUW9rOGczNF9PQUJvJnZuPTI4OCZsb2dpbnR5cGU9MCZjdj0zJnN3dj01JmNsaT01</URL><O>PRE</O><T>TOP</T><HIP>113.108.212.38</HIP></Rq></R></RepLookup>",
        LAST);

    web_custom_request("urs.asmx_4",
        "URL=https://urs.microsoft.com/urs.asmx?MSURS-Client-Key=r/4pztMEzQOon4ZLeymWxw%3d%3d&MSURS-Patented-Lock=a7q4TZRzKXk%3d",
        "Method=POST",
        "Resource=0",
        "RecContentType=text/xml",
        "Referer=",
        "Snapshot=t26.inf",
        "Mode=HTML",
        "EncType=text/xml; charset=utf-8",
        "Body=<RepLookup v=\"3\"><G>ED8654D5-B9F0-4DD9-B3E8-F8F560086FDF</G><O>F03F2D77-79E1-4DEC-BBF8-81A5C0790160</O><D>9.0.8110.0</D><C>9.00.8112.16421</C><OS>6.1.7601.1.0</OS><I>9.0.8112.16421</I><L>zh-CN</L><R><Rq><URL>aHR0cDovL20ubWFpbC4xMDA4Ni5jbi9idjEvaG9tZS5odG1sP21vPVUwajVHdXZWYUxHM1h6MHFNaWJiUW9rOGczNF9PQUJvJnZuPTI4OCZsb2dpbnR5cGU9MCZjdj0zJnN3dj01JmNsaT01</URL><O>POST</O><T>TOP</T><HIP>113.108.212.38</HIP></Rq></R></RepLookup>",
        LAST);

    lr_end_transaction("登陆",LR_AUTO);  //登录事物结束

    return 0;
}


 控制器中设置集合点策略

  我们在Virtual User Generator 中回放脚本无法体现集合点的作用。集合点是在多用户并发运行的时候才能起作用。所以,我们需要把脚本导入到Controller 中进行进一步的设置。

  1、打开Controller (控制器),导入录制的脚本。

  2、菜单栏 Scenario ---> Rendezvous 打开集合点设置界面

  我们可以看到Vusers 列表框里有10个集合点(1~10),这里的数量和我们设置的虚拟用户数一致。

  我们可以点击选中某个集合点,点击“Disable VUser” 按钮,使其不参与集合点。(某班级早晨集合跑步,A同学肚子痛,经过老师的允许不参与早晨的集合跑步。)

  点击“Policy...”来设置集合点的策略:

  下面来看看这三种策略的含义:

  Release when :当所有虚拟用户中的x % 到达集合点进释放,即仅当指定百分比的虚拟用户到达集合点时,才释放虚拟用户。

  注意:此选项将会干扰场景的计划。如果选择此选项,场景将不按计划运行。

  Release when :当所有正在运行的虚拟用户中的x %到达集合点时释放,即仅当场景中指定百分比的、正在运行的虚拟用户到达集合点时,才释放虚拟用户。

  还有不在运行的虚拟用户? 假如,设置为1分钟启动一个用户,当然会存在因为用户还没启动,所以无法参与集合点。

  Release when : 当x 个虚拟用户到达集合点时释放,即仅当指定数量的虚拟用户到达集合点时,才释放虚拟用户。

  这个很好理解,当我用百分比不太好衡量集合点的虚拟用户数,当然可以设置具体的用户数。

  Timeout between Vusers (虚拟用户之间的超时)框中输入一个超时值。

  假如设置了集合10用户并发,结果9个用户已经集合到位,还剩1个虚拟用户,左等右等就是等不来。那总不能一直等下去吧。设定了个时间,假如30秒还不来,那就不管它了。

  超时的时长默认是30秒,我们可以根据具体的被测应用进行调整。

相关链接:

LoadRunner 技巧之协议分析

LoadRunner 技巧之THML 与 URL两种录制模式分析

LoadRunner 技巧之思考时间设置

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

LoadRunner 技巧之自动关联

这一节讲loadunner 关联的问题,其实这个东西理解起来简单,但说起来比较麻烦。

  关联的原理:

  先来模拟一个场景,我去坐火车,坐火车要先检票,检票员核对火车票的时间、班次等信息正确后允许我坐火车。

  过了几天,我又拿着一张票去坐火车,检票员再一次核对火车票信息,发现这张票过期了,肯定不让我坐了。那我怎么才能坐火车呢?我先看看旁边那哥们的火车票上的信息,然后把自己的查票涂改成和他一模一样再去坐火车。

  在我们录制测试脚本时也经常会遇到这样的情况:录制的时候,服务器会给一个唯一的认证码来进行操作,当再次回放脚本的时候服务器又会给一个全新的认证码,而脚本录制是死的,还是拿老的认证码提交,从而导致脚本执行时失败。

  下面详细来分析一下录制与回放的过程

  录制过程:

  1、输入用户名密码登录

  2、服务器端返回一个sesiionID@@@12345

  3、客户端拿着获得sesiionID@@@12345进一步请求服务器信息。

  4、服务器返回客户端想要的信息

  回放过程:

  1、输入用户名密码登录

  2、客户端返回新的sesiionID@@@23456

  3、因为脚本中的sesiionID@@@12345 是写死的,所以我们会依然拿着老的sesiionID@@@12345去向服务器请求信息

  4、服务器你经过验证发现你的sesiionID@@@12345 是错误的。

  为了确保脚本回放的成功,我们需要获得服务器每次返回的动态的sesiionID,再将这个动态数据发回给服务器。而关联能够帮助我们将服务器返回的数据进行处理并保存为参数。

  OK!通过上面的分析,我们大概明白的关联的原理,下面要解决的问题就是如何设置关联。关联的三种方法:

  ● 自动关联

  ● 手动关联

  ● 一边录制一边关联

 自动关联

  在录制脚本之后打开recording Options 窗口,点击correlation标签页。确保Enable correlation during recording 选项处于勾选状态。

  在LoadRunner 自带WebTours为例设置关联,

  访问WebTours首页,点击administration 链接,设置 Set LOGIN form's action tag to an error page.勾选更新。

  录制WebTours登录与退出脚本:

Action()
{

    web_url("WebTours", 
        "URL=http://127.0.0.1:2080/WebTours/", 
        "Resource=0", 
        "RecContentType=text/html", 
        "Referer=", 
        "Snapshot=t1.inf", 
        "Mode=HTML", 
        LAST);

    web_url("favicon.ico", 
        "URL=http://127.0.0.1:2080/favicon.ico", 
        "Resource=1", 
        "Referer=", 
        LAST);

    web_submit_data("login.pl", 
        "Action=http://127.0.0.1:2080/WebTours/login.pl", 
        "Method=POST", 
        "Referer=http://127.0.0.1:2080/WebTours/nav.pl?in=home", 
        "Mode=HTML", 
        ITEMDATA, 
        "Name=userSession", "Value=110381.833940867fzHHHzfpfiDDDDDDDtAzzpfQDf", ENDITEM, 
        "Name=username", "Value=test", ENDITEM, 
        "Name=password", "Value=123456", ENDITEM, 
        "Name=JSFormSubmit", "Value=on", ENDITEM, 
        LAST);

    web_submit_data("login.pl_2", 
        "Action=http://127.0.0.1:2080/WebTours/login.pl", 
        "Method=POST", 
        "RecContentType=text/html", 
        "Referer=http://127.0.0.1:2080/WebTours/nav.pl?in=home", 
        "Snapshot=t2.inf", 
        "Mode=HTML", 
        ITEMDATA, 
        "Name=userSession", "Value=110381.833940867fzHHHzfpfiDDDDDDDtAzzpfQDf", ENDITEM, 
        "Name=username", "Value=test", ENDITEM, 
        "Name=password", "Value=123456", ENDITEM, 
        "Name=JSFormSubmit", "Value=on", ENDITEM, 
        "Name=login.x", "Value=52", ENDITEM, 
        "Name=login.y", "Value=1", ENDITEM, 
        LAST);

    web_image("SignOff Button", 
        "Alt=SignOff Button", 
        "Snapshot=t3.inf", 
        LAST);

    return 0;
}




 回放脚本出错:

Action.c(47): Error -27987: Requested image not found      [MsgId: MERR-27987] Action.c(47): web_image("SignOff Button") highest severity level was "ERROR", 0 body bytes, 0 header bytes      [MsgId: MMSG-26388]

  选择菜单栏Vuser ---> Scan Script for Correlations (快捷键ctrl + F8 )弹出下面窗口

  扫描相关性可能需要几分钟,你想继续么? 当然,点击“YES”

  选中需要关联的内容,点击“ remove Correlation ”

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

LoadRunner 技巧之手动关联与预关联

 上一节介绍了关联的原理与自动关联,除了自动关联还有另外两种关联方式:手动关联与预关联。

  手动关联

  如果脚本很长,那么我们想找到一个脚本中哪些地方是需要关联的并不是一件容易的事情。这时,我们可以通过脚本对比的方法找出需要关联的内容。

  1、录制第一份脚本,保存为“关联a” 。

  2、录制第二份脚本(操作步骤与第一次保持一致),调用loadrunner自带的WinDiff

  (Tools-->campare with users ),选择“关联a”

  通过WinDiff 对脚本的对比,我们很容易来判断需要做关联的内容。

  3、使用web_reg_save_param函数建立关联脚本

web_reg_save_param(
        "ParamName=CorrelationParameter_1",
        "LB=userSession Value=",
        "RB=>",
        "Ordinal=1",
        "RElFrameId=1.2.1",
        "Scope=Body",
        LAST);

  int web_reg_save_param( const char *ParamName, <List of Attributes>, LAST );

  第一部分(const char *ParamName):参数名,用双引号括起来,逗号分开

  第二部分(<List of Attributes>):包括LB、RB、RelFameID、Ord、Search、SaveOffiset、SaveLen等。

  第三部分(LAST ):结束标志。

  通过按F1 键打开帮助文档,搜索此函数,查看具体用法。

  4、LR11 还提供了另一种手动关联方式。

  选择Tree 视图模式,点击HTTP View 下查看Response Body 中,打到需要关联的内容,右键在下拉列表中选择“Create Coorelation”



 预关联

  预关联也可以叫做“ 一边录制一边关联”。为什么录制某此系统,会得到一些自动关联函数呢?这就是系统默认提供自动关联设置。如果我们预先知道需要关联的内容时。可以预先设置好要关联的内容,这样在脚本录制的过程中自动对要关联的内容进行关联。

  在录制脚本时打开Recording Options 设置窗口

  (本文以loadrunner 自带WebTours 的关联为例)

  1、点击一New Application 按钮,新建一个叫做WebTours 的应用

  2、选择这个规则,点击 New Rule 按钮一个规则。

  3、然后做如下设置:

  关联函数web_reg_save_param_ex

  前面的例子用到web_reg_save_param 关联函数,web_reg_save_param_ex 可能也是很常用的一个关联函数。我们碰到函数就胆怯,这个不知道暗具体怎么使用它。

  菜单栏Insert --- new step 打开add step 窗口

  搜索web_reg_save_param_ex 函数,选中函数,点击OK,弹出函数的设置窗口:


 Prameter name:此设置存放参数的名称。

  Left boundary:此处设置左边界,这里是用来填写关联对于数据处理的左匹配内容规则。

  Match case:默认情况下边界是检查Match case ,也就是检查大小写的。

  Binary data:如果要关联的内容是非ASCII 字符的,需要选择此项

  Regular expression:在LR11 中关联提供了使用正则表达式的功能,但是LR11 Patch3 中取消web_reg_save_param_ex 函数对此功能的设置。

  Reight boundary:此处设置右边界。

  DFEs:在录制选项和回放中我们提供了DFE的功能,在关联这里也支持DFE的数据处理。(DFE等后面再解释,或你自行查资料)

  Ordinal:这个关键字在很多函数里面都有应用,在这里可以填写任意一个整数,也可以填All。如果填写数字,那么说明从返回的记录中取出对应顺序的值,而填写All的话将会返回所有内容。

  Save Offset:设置关联的内容偏移量,从第几位开始进行关联操作。

  Save Length:关联出来的内容所需要保存的长度。

  Warm if text was not found (Default is Error):对于关联的对象不存在的处理。

  Filters:下面的选项都是帮助关联返回限定的,通过这些设置可以进一步减少返回的范围。

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

  关于关联函数的每一个选项的介绍,我将得不是很清楚,你可以找更详细的资料学习。

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

如何有效发现UI用户界面层的缺陷

【UI型Bug定义】

  这里指的UI型指以下两种Bug:

  第一种是文字型Bug,即和给定的字符资源不一致的Bug,比如文字/字符/提示语/引导语/用户协议等文字方面的不一致。

  第二种是UI效果不一致的Bug,比如应该是个圆角按钮,做出来的界面却是个平角的按钮;有下拉箭头效果,做出来的界面却没有下拉箭头效果;混动界面应该有3屏,做出来的界面却只有2屏,诸如此类。

  【UI型Bug的产生】

  理论上UI型Bug的产生只有一种原因,即开发人员没有按照需求文档或者UI做。

  开发人员为什么没按需求的要求去实现?通常有两种原因:

  第一种,开发人员一开始就没按需求实现;

  第二种,需求方频繁变更,没来得及更新文档。

  在敏捷Agile场景下,开发人员会把最主要的原因归为需求方没有及时更新文档。

  【如何减少UI型Bug】

  理想情况下,所有的环节都按文档做,是不存在所谓的UI型Bug的。即需求方确定文档,开发人员严格按照文档实现。

  UI文档的变动要及时更新并通知到开发人员和测试人员。

  开发人员要严格按照文档的需求去开发,不能主动发挥(任何的主动发挥都要征得需求方的同意并通知到下一个环节(即测试环节)的人员)。

  【QA人员碰到很多的UI型Bug该怎么办?】

  当UI型Bug占到Bug总数的一定数量后,QA人员有义务想产品或者项目经理提出抗议,说明这是在浪费大家的时间。

  【AgileHei测试是怎么做的?】

  测试人员不负责测试UI型Bug,由需求方在验收时统一对UI进行验收(或者在项目中期约定一个时间进行修改)。UI型Bug是很直观的Bug,由需求方和实现方直接达成协议,结对及时修改最有效率。

  【结论】

  UI型Bug是沟通不一致的产物,测试人员不要把有限的精力放在UI型Bug上面。为追求所谓的敏捷而导致了后续环节的频繁沟通,不是Agile的本意,是为了敏捷而敏捷,为了增加沟通而沟通。

posted @ 2013-03-12 10:59 顺其自然EVO 阅读(172) | 评论 (0)编辑 收藏

Android集成测试

 Android集成测试主要是在单元测试的基础上测试接口访问或者异步任务是否正确,在移动凤巢系统中,大概有30+个接口需要测试,他们都遵循一个特定的访问模式:前台的Activity获取到触发事件后,将它传给这些接口,这些接口都是AsyncTask的实现——即后台异步线程执行某个任务(一般是发送http请求到后端服务或者执行存取数据库等耗时操作),完毕后调用回调函数,示意图如下:

  一、测试框架

  对于Android中这种异步接口的自动化测试需要解决3个问题:

  1)如何获取到异步任务执行结果;

  2)如何让上层测试代码尽量不处理任务等待;

  3)如何处理需要登录的接口。

  对于问题1)每个异步任务在获取结果后就直接调用onPostExecute()方法了,测试代码获取不到结果,所以必须有一个专门的桩Activity负责异步任务的执行并将结果暴露出来;

  对于问题2)尽量将等待操作交给测试基类,上层测试代码只需要执行被测逻辑;

  对于问题3)采用模板模式,如果接口需要登录则先执行登录操作后再调用,整体解决方案如下:

  其中桩Activity的逻辑如下,它实际决定接口的调用方式,如果被测接口需要登录则先登录后调用,如果不需要登录则直接调用。



  可以看到isCompleted是标识异步任务是否执行完毕的,无论异步任务返回是onSuccess、onError还是onIOException都会进行置位;result则是异步调用的返回,可以看到这里无论接口调用是成功、失败还是io异常都会将这个结果暴露出来以使测试代码能够获取到;

  interfaceAction是测试代码需要实现的接口,内容如下:

  由于有些接口是必须登录才能访问的,所以前两个接口实现是为其服务的,如果访问的接口必须处于登录状态则先调用login()方法,然后再调用实际的action()方法。另外,书写测试代码的时候为了减少对异步任务等待的代码以及显示对InterfaceActivity这个桩Activity

  的调用还需要完善测试基类:这样上层test case只需要关心具体的测试逻辑而不用关心异步调用及等待处理

 二、测试方法

  以商桥访问接口测试为例,由于它是一个需要登陆才能访问的接口,写自动化case的时候需要完成两个类:BridgeAction和BridgeTest。其中BridgeAction主要是接口的调用,BridgeTest是测试内容:

  这里BaseLoginAction实现了needLogin()和login()方法,主要是登录逻辑,BridgeAction的action()是真正接口的调用。

  测试类主要是对BridgeAction的调用,它可以向接口传递不同的参数,同时也可传递登录操作的用户名和密码。waitAsyncTaskComplete(action)完成了接口逻辑的调用及时间的等待,测试代码只需着重关注result并进行断言。

  三、总结

  异步接口的集成测试的侧重点在于Android手机端向服务器端发送的请求是否正确,以上测试CASE的断言与后端数据其实是强耦合的,即后端caiye这个账户的数据变化可能导致CASE的fail,所以可以考虑引入hamcrest包,做一些匹配校验,主要测试正常和异常情况服务器返回的内容是否符合预期,比如上面最后一个断言可以写成:assertEquals(intValue(),greaterThan(0));当然,服务器端接口的正确性正常情况下应该由服务器端的自动化Case来保证,这样才不至于前后端测试紧耦合在一起。


posted @ 2013-03-12 10:46 顺其自然EVO 阅读(849) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 265 266 267 268 269 270 271 272 273 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜